简体   繁体   中英

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?

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.

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.

You can write methods in Class A and B which synchronizes on the mutex called motor as below:

class A{

public void doSomething(){
    synchronized(MotorA.motor){
        //do what you want
    }
}



class B{

public void doSomething(){
    synchronized(MotorA.motor){
        //do what you want
    }
}

Here is the class for MotorA

class MotorA{
    public static MotorA motor=new MotorA();
    ......
    ......
    ......
}

EDIT

If you already have a instance and want to share among class A and B than you can pass the object in constructure and synchronize on the object:

class A{
    final MutexObject mm;
    public A(MutexObject m){
        mm=m;
    }
    public void doSomething(){
        synchronized(mm){
            //do what you want
        }
    }



class B{
final MutexObject mm;
public B(MutexObject m){
    mm=m;
}
public void doSomething(){
    synchronized(mm){
        //do what you want
    }
}

But be careful while passing the object of MutexObject into the constructers of A and B , it should be same instance.

This is not a direct answer to the question you asked, just some advice.

I suggest you make your "Motor" a class in itself. You don't have to give either of your other classes "Control" of the motor variable itself, they just both have references to the motor class. You can synchronize each method on the motor class so that one method will have control until a sequence of commands is complete, then release control as the method returns.

I've NEVER see more classes (or methods) make code more complex--I've heard it can happen, but I've never seen it. The complexity I've seen always comes from trying to do things without creating new methods or classes.

The methods on your motor class can make more sense as well. For instance (Sorry, I have no idea how this works, so I'm just guessing) if you activate the motor by setting a bit, you can expose method like "goForward(int speed)" and "goBack(int speed)" instead of "motor|=0x10" or something obscure like that.

Instead of synchronizing at the end user class (ClassA & ClassB), you can do it in your motorA class itself. It should be the MotorA class's responsibility to synchronize its actions.

MotorA class :

public class MotorA {

    private static MotorA instance = new MotorA();
    private static int pointer = 0;

    private MotorA() {
    }

    public static MotorA getInstance() {
        return instance;
    }


   public void rotate() {
       synchronized(MotorA.class) {
           System.out.println(Thread.currentThread() + " Rotating "+ pointer++);
           try {
              Thread.sleep(100);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
       }
   }

}

End user of the MotorA class - ClassA & ClassB which doesn't know much about the existences of the other threads.

public class ClassA implements Runnable {

    @Override
    public void run() {
        doRotate();

    }

    private void doRotate() {
        MotorA motor = MotorA.getInstance();
        while (true) {
            motor.rotate();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


public class ClassB implements Runnable {

    @Override
    public void run() {
        doRotate();

    }

    private void doRotate() {
        MotorA motor = MotorA.getInstance();
        while (true) {
            motor.rotate();
            try {
                Thread.sleep(15);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

Here the Main program.

public class Main {
    public static void main(String[] args) {

        Thread a = new Thread(new ClassA());
        Thread b = new Thread(new ClassB());
        Thread c = new Thread(new ClassA());
        Thread d = new Thread(new ClassB());
        a.start();
        b.start();
        c.start();
        d.start();
    }
}

How about this:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Shared<T> {
    private T _data;
    private final Lock lock = new ReentrantLock(true);

    public T getData() {
        try {
            lock.lock();
            return _data;
        } finally {
            lock.unlock();
        }
    }

    public void setData(T _data) {
        try {
            lock.lock();
            this._data = _data;
        } finally {
            lock.unlock();
        }
    }
}

Usage:

Shared<Boolean> b = new Shared<Boolean>();
b.setData(true);

//other thread
while (b.getData()) {
  ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM