【问题标题】:Synchronizing a shared variable among threads in DIFFERENT classes in java?在java中不同类的线程之间同步共享变量?
【发布时间】:2013-11-09 17:35:50
【问题描述】:

这是给我的 NXT 积木的。

假设我有两个不同的类,A 类和B 类,每个类都有自己的线程在运行。

但是,两个类共享一个名为MotorA 的静态变量实例。这个MotorA 变量是一个物理电机,它的运动可以由两个类控制。 A 类和B 类的线程都可以控制MotorA 的运动,但我只希望它们中的一个同时控制MotorA

例如,如果 A 类试图向前旋转 MotorAB 类试图向后旋转 MotorA,我只希望 A 类向前旋转 MotorA 并阻止 B 类效果。

问题:我可以使用 SAME 锁来同步来自不同类的线程中的方法吗?

【问题讨论】:

  • 是的,你可以;每个资源都应该被一个锁保护。 (尽管拥有一个封装对电机及其锁的访问的类可能会更好。)
  • 由于电机变量在两个线程之间共享,synchronize 在该电机变量上,这是完全合法的。
  • 虽然MotorA 是静态的,所以使方法同步没有帮助(除非方法也是静态的),您需要锁定方法内的变量。

标签: java multithreading synchronization locking


【解决方案1】:

是的,你可以。您实际上可以这样做:

  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 */

 }

接下来,当你想对 MotorA 实例做任何操作时,你只需要:

  • 1) 通过getLock() 检索其锁
  • 2) 在给定实例上调用 Lock.lock()
  • 3) 通过getMotorA() 获取MotorA 单例
  • 4) 对MotorA instace 执行任何方法
  • 5) 调用 Lock.unlock() 释放锁。

在这种情况下,对资源的访问将不受多个线程的影响。

或者您可以简单地在 MotorA 的实例上进行同步:

  class UserOfMotor1 {

    public void doOperationInMotor1Thread() {

        synchronized(MotorA.getMotorA()) {

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

        }
    }

  }

但在这种情况下,您还必须在线程使用共享资源时使用synchronized() 块 - 在您的情况下为MotorA。如果您使用这种控制同步的方法,您必须确保您在不同线程中对同一个对象进行同步 - 在这种情况下,MotorASingleton,因此您始终获得相同的实例。

【讨论】:

  • 非常感谢!我可以通过将所有代码放入同步(锁定){}中而不是执行锁定和解锁操作来实现相同的效果吗?
  • 是的。但这将是一种不同的机制——但仍然是相同的结果。您可以对任何对象进行 synchronize()。在您的情况下,在 MotorA 实例上同步会更容易。 Lock.lock() 是与 synchronized() 不同的机制,尽管在大多数情况下执行相同的操作。使用哪一个只是一个偏好问题,尽管 Lock 在您有多个同步块的情况下更灵活。
  • 您好,我仍然不太明白同步(this)和同步(MotorA)之间的区别。使用虚拟对象作为同步锁与使用我想要独占访问的实际对象相比有什么区别?谢谢!
  • this 关键字表示您引用对象,从中调用方法。如果您在 MotorA 类中引用“this”,您将与“MotorA”上的同步。您可以使用虚拟对象。有很多方法。但是您需要在使用 MotorA 的所有线程中同步此虚拟对象。这只是选择的问题。我只是写了一个例子。
  • 另外,我想问一下:如果我想让B类覆盖A类的控制,我该怎么做?例如,A 类螺纹使电机连续正转,B 类有一种使电机反转的方法。如果 A 类当前正在使电机正转,我调用 B 类方法,我希望电机:开始将电机向后旋转一段时间,然后当 B 类方法完成后,恢复为正转。 (还给A级电机控制)
【解决方案2】:

每个线程都可以synchronize 在电机实例上使用它。但是,如果一个线程将同时使用多个电机,这将导致您获取每个电机上的锁。那么您必须非常小心嵌套同步块的顺序,否则您将遇到间歇性死锁。

相反,我建议您为所有电机使用一个锁,并且切勿在未握住该锁的情况下使用任何电机。由于电机是静态资源,因此该锁也可能是静态资源。在大型程序中,创建自己的私人锁并仔细控制谁可以访问它会更安全一些。在这种情况下,拥有一个任何代码都可以访问的全局锁变量可能没问题。

final class Locks {

  public static final Object MOTORS = new Object();

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

}

使用它:

final class ThreadA extends Thread {

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

}

您无需将自己局限于电机;关键是不要让嵌套块在不同的对象上同步。这可能直接发生在一种方法中,或者因为您从另一个方法中的同步块中调用具有同步块的方法。

【讨论】:

    【解决方案3】:

    您可以在 AB 类中编写方法,它们在名为 motor 的互斥体上同步,如下所示:

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

    这是MotorA的类

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

    编辑

    如果您已经有一个实例并希望在 AB 类之间共享,那么您可以在构造中传递对象并在对象上同步:

    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
        }
    }
    

    但是在将MutexObject 的对象传递给AB 的构造函数时要小心,它应该是同一个实例。

    【讨论】:

    • 如果 MotorA 实际上是另一个名为 NXTRegulatedMotor 的类的实例,这是否有效?那个类不是我发明的,它带有 leJOS(我正在尝试编程的 NXT 砖的 Java 虚拟机!)我的意思是:NXTRegulatedMotor MotorA = Motor.A;
    • 它会起作用,你只需要确保你在AB类之间共享同一个实例。并在同一个实例上同步。还有什么需要的吗?
    【解决方案4】:

    这不是对您提出的问题的直接回答,只是一些建议。

    我建议您将“电机”本身设为一个类。您不必为其他任何一个类提供电机变量本身的“控制”,它们都具有对电机类的引用。您可以同步电机类上的每个方法,这样一个方法将拥有控制权,直到完成一系列命令,然后在方法返回时释放控制权。

    我从未见过更多的类(或方法)会使代码变得更复杂——我听说它会发生,但我从未见过。我看到的复杂性总是来自于尝试在不创建新方法或类的情况下做事。

    您的电机类中的方法也更有意义。例如(对不起,我不知道它是如何工作的,所以我只是猜测)如果你通过设置一点来激活电机,你可以暴露像“goForward(int speed)”和“goBack(int speed)”这样的方法而不是“motor|=0x10”之类的晦涩难懂的东西。

    【讨论】:

      【解决方案5】:

      您可以在自己的 motorA 类中进行同步,而不是在最终用户类(ClassA 和 ClassB)中进行同步。同步其动作应该是 MotorA 类的责任。

      MotorA 类:

      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();
                  }
             }
         }
      
      }
      

      MotorA 类的最终用户 - ClassA 和 ClassB,他们不太了解其他线程的存在。

      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();
                  }
              }
          }
      
      }
      

      这里是主程序。

      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();
          }
      }
      

      【讨论】:

        【解决方案6】:

        这个怎么样:

        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();
                }
            }
        }
        

        用法:

        Shared<Boolean> b = new Shared<Boolean>();
        b.setData(true);
        
        //other thread
        while (b.getData()) {
          ...
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-11-14
          • 1970-01-01
          • 2019-05-01
          • 2019-06-20
          • 2013-08-10
          • 2011-01-08
          相关资源
          最近更新 更多