我的类是否是线程安全的,多个线程访问其变量?

时间:2012-10-10 18:36:45

标签: java concurrency thread-safety synchronized

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

  • 最终变量由于是不可变的而是线程安全的。
  • 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 个答案:

答案 0 :(得分:6)

不,这不是线程安全的。如果使用AnObject方法,您确保只有一个线程可以一次更改changeObj()中的值,但是您还为此对象提供了一个getter,因此任何其他线程都可以调用{{1}并发。

答案 1 :(得分:2)

你的类本身在当前状态下是线程安全的(假设这里没有显示任何方法),但是你的思维中可能有一个“bug”;

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

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

最终变量不一定是线程安全的,只有不可变的最终变量是线程安全的 - 它涵盖了诸如String之类的原语和类,或者本身是线程安全的类的最终变量。

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

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

主题A

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

Thead 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实例,而是返回该实例的副本。