2

当我试图掌握线程安全时,我想知道这个类是否是线程安全的?如果两个线程随时尝试访问其变量,在我看来它是线程安全的,因为:

  • final 变量是线程安全的,因为它是不可变的。
  • getter 和 setter 是同步的,所以 mObject 一次只能被一个线程获取或设置。因此,两个线程不可能读取不同的值。
  • 方法changeObj() 是不同步的,但是其中处理类变量(即mObject)的任何块都是同步的。

如果我错了或者这个类不是线程安全的,请告诉我。

public class MyClass{

   private final String = "mystring"; //thread safe because final
   private AnObject mObject;

   public MyClass(){
       //initialize
   }

   //only one class can set the object at a time.
   public synchronized void setObj(AnObject a){
      mObject = a;
   }

    //two threads trying to get the same object will not read different values.
   public synchronized AnObject getObj(){
      return mObject;
   }

   //better to synchronize the smallest possible block than the whole method, for performance.
   public void changeObj(){
       //just using local variables (on stack) so no need to worry about thread safety
       int i = 1;
       //i and j are just to simulate some local variable manipulations.
       int j =3;
       j+=i;
       synchronized(this){
          //change values is NOT synchronized. Does this matter? I don't think so.
          mObject.changeValues();
      }
   }
}
4

4 回答 4

6

不,它不是线程安全的。AnObject如果使用该方法,请确保一次只有一个线程可以更改您的值changeObj(),但您还为此对象提供了一个 getter,因此任何其他线程都可以changeValues()同时调用。

于 2012-10-10T18:41:13.970 回答
2

您的类本身在其当前状态下是线程安全的(假设此处未显示任何方法),但是您的想法中可能存在一个“错误”;

mObject不是 100% 封装的,它是通过 setter 传入的,并且可以通过 getter 获取。这意味着任何线程都可以同时获得对 mObject 引用的对象的引用和调用方法,而 MyClass 并不知道它。

换句话说,AnObject 的方法可能也需要是线程安全的。

至少,MyClass 中的同步不会以任何方式使 mObject 免受线程问题的影响

于 2012-10-10T18:43:58.180 回答
1

除了 JB Nizet 先生的观点之外,如果AnObject.changeValues是一种设计为被客户端覆盖或调用此类方法的方法,那么在一般情况下,这为各种不需要的行为打开了大门,例如死锁和数据损坏。您绝不能将控制权交给同步块中的外来代码。“外星人”是指不受您控制的代码。更多细节可以在 Effective Java, 2nd Ed, Item 67 中找到。

于 2012-10-10T18:46:50.970 回答
1

final 变量不一定是线程安全的,只有不可变的 final 变量是线程安全的——包括原语和类String,或者本身是线程安全的类的 final 变量。

您的类不是线程安全的,因为它公开了变量a,但也需要它进行内部工作。

下面的示例将演示如何a进入不一致状态的示例。

线程 A

MyClass myClass = ...
myClass.changeObj(); 
// imagine Thread A is suspended during the synchronized block inside of 
// changeObj()

头B

MyClass myClass = ...
AnObj anObj = myClass.getObj();
anObj.changeValues();
// uh-oh, I am modifying the state of this instance of anObj which is also 
// currently being modified by Thread A

要使 MyClass 真正成为线程安全的,您必须执行以下操作之一。

  • AnObj 还必须保证线程安全(通过使修改其状态的方法成为线程安全的)
  • AnObj 必须是不可变的。也就是说,如果您需要修改 AnObj 的状态,您必须创建 AnObj 的新实例来保持新状态。
  • AnObj 的 getter 不能直接公开 MyClass 的 AnObj 实例,而是返回该实例的副本。
于 2012-10-10T18:50:09.607 回答