Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronizing a shared variable among threads in DIFFERENT classes in java?

this is for my NXT brick.

Let's say I have two DIFFERENT classes, Class A and Class B, and each class has its OWN thread running.

However, there is a static variable instance called MotorA that is shared by both classes. This MotorA variable is a PHYSICAL motor whose motion can be controlled by the two classes. Both Class A and Class B's threads can control MotorA's motion but I only want ONE of them to control MotorA at a time.

For example, if Class A is trying to rotate MotorA forward and Class B is trying to rotate MotorA backward, I only want Class A to rotate MotorA forward and block Class B's effect.

Question: Can I use the SAME lock to synchronize methods in threads from DIFFERENT classes?

like image 563
user2973438 Avatar asked Oct 03 '22 11:10

user2973438


2 Answers

Yes, you can. You can actually do it like this:

  class MotorA {

    private static MotorA motor = new MotorA(); 
    private static final java.util.concurrent.locks.Lock lock = new java.util.concurrent.locks.ReentrantLock();

    private MotorA() { }

    public static MotorA getMotorA() {
    return motor;
    }

    public static Lock getLock() {
    return lock;
    }

    /* here go business methods for MotorA */

 }

Next, when you want to do any operation on MotorA instance, you just need:

  • 1) retrieve its lock by getLock()
  • 2) Call Lock.lock() on the given instance
  • 3) acquire MotorA singleton by getMotorA()
  • 4) perform any method on MotorA instace
  • 5) Call Lock.unlock() to release the lock.

In this case, the access to the resource will be safe from multiple threads.

Or you can simply synchronize on the instance of MotorA:

  class UserOfMotor1 {

    public void doOperationInMotor1Thread() {

        synchronized(MotorA.getMotorA()) {

            MotorA motor = MotorA.getMotorA();
            motor.soSth();

        }
    }

  }

But in this case, you will also have to use synchronized() block in whenever thread uses the shared resource - in your case MotorA. If you use this method of controlling synchronization, you must ensure that you are synchronizing on the same object in different threads - in this case MotorA is a Singleton so you always get the same instance.

like image 189
Artem Moskalev Avatar answered Oct 13 '22 11:10

Artem Moskalev


Each thread can synchronize on the motor instance while its working with it. However, if one thread is going to be using multiple motors together, this would lead you to acquire locks on each motor. Then you'd have to be very careful about the order you nest your synchronized blocks, or you will have intermittent deadlocks.

Instead, I suggest that you use a single lock for all motors, and never use any motor without holding that lock. Since the motors are static resources, this lock could be too. In a big program, it would be a bit safer to create your own private lock, and carefully control who can access it. In this context, it's probably okay to have a global lock variable that any code can access.

final class Locks {

  public static final Object MOTORS = new Object();

  private Locks() { /* Disallow instantiation. */ }

}

To use it:

final class ThreadA extends Thread {

  public void run() {
    ...
    synchronized(Locks.MOTORS) {
      Motor motorA = Motors.A;
      motorA.setSpeed(...);
      ...
    }
  }

}

You wouldn't need to limit yourself to just motors; the key is not to have nested blocks synchronizing on different objects. That might happen directly in one method, or because you call one method with a synchronized block from a synchronized block in another method.

like image 45
erickson Avatar answered Oct 13 '22 12:10

erickson