我有以下代码
//编辑: - 使用@Riaz的答案更新代码(此代码现在应该是线程安全的)
public final class MyClass
{
private static MyClass2 _class2;
private MyClass()
{
}
public static synchronized MyClass CreateMyClass1(String arg0 , ArrayList<MyClass3> class3) throws Exception
{
MyClass myClass = new MyClass();
_class2 = new Class2(arg0 , class3);
return myClass;
}
public static synchronized MyClass CreateMyClass2(InputStream arg0 , ArrayList<MyClass3> class3) throws Exception
{
MyClass myClass = new MyClass();
_class2 = new Class2(arg0 , class3);
return myClass;
}
//EDIT :- added a third method that accesses methods of the _class2 object
public Object getSomething() //don't need synchronized for methods that don't change the state of the object
{
return MyClass._class2.someMethod();
}
public synchronized Object doSomethingToClass2()
{
//change state of the _class2 variable
}
}
我已经阅读了几篇解释静态方法的线程安全的帖子,但我有几个问题:
据我所知,除非两个线程可以改变状态 一个共享的可变对象,我不需要担心线程 安全。 (假设我没有泄露“this”引用。)
因此,当使用MyClass时,thread1可以调用CreateMyClass1和thread2 调用CreateMyClass2,这意味着_class2变量将 由thread2改变,这是我想要避免的。会制作 _class2 as volatile阻止这个?如果是,我不确定静态不稳定 将由JVM解释? 这足以使MyClass线程安全吗?
在两个静态方法中返回MyClass类的对象 导致任何违反线程安全的行为?
答案 0 :(得分:1)
因此,当使用MyClass时,thread1可以调用CreateMyClass1和thread2 调用CreateMyClass2,这意味着_class2变量将是 由thread2改变,这是我想要避免的。
静态变量在线程之间有效共享。但是,一个线程的更改不会被其他线程自动看到。将变量声明为volatile
可确保可见性。
我不确定JVM如何解释静态volatile?
这里有一个来自JVM规范的粘贴,可以解释所有内容并确认您的理解。
ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.
在各自的顺序中,参数是标志名称,值,解释。
在静态中返回MyClass类的对象 方法导致任何违反线程安全的行为?
根本没有在方法范围中实例化对象,它将作为任何其他对象处理。
有关可能有用的线程安全的更多详细信息
synchronized
添加到静态方法时,Class
对象将被锁定。答案 1 :(得分:1)
static表示与包含类的实例无关。这意味着所有对象(和静态方法)共享同一个变量。
volatile只是意味着其他线程可能会在没有警告的情况下更改该值。
将变量声明为volatile(无论是否为静态)表明该变量将被多个线程频繁访问。在Java中,这可以归结为指示线程无法缓存变量的值,但必须在变异后立即回写,以便其他线程看到更改。 (默认情况下,Java中的线程可以自由缓存变量。)
因此这两个修饰符的效果完全正交。您可以将变量初始化为static volatile
答案 2 :(得分:1)
考虑各种关键词的含义/含义;
volatile
- 它保证读取该字段的任何线程都会看到最新/最近写入的值(这仅在您需要共享可变数据时才有用)
synchronized
- 获取对同步内容的锁定,以便其他线程无法执行任何操作,直到具有锁定的线程完成所需的操作
鉴于您的CreateMyClass1
和CreateMyClass2
方法都将_class2
重新分配给MyClass2
的新实例,不同的主题可能会更改_class2
。
如果您想为每个线程分别设置一个值,可以考虑查看ThreadLocal
。也许使用静态ThreadLocal
可能适合您的需求;
private static ThreadLocal<MyClass> threadLocal = new ThreadLocal<MyClass>();
这会取代private static MyClass2 _class2;
然后在create方法中,您可以设置每个线程MyClass2;
public static MyClass CreateMyClass1(String arg0 , ArrayList<MyClass3> class3) throws Exception
{
Test myClass = new Test();
threadLocal.set(new MyClass2(arg0 , class3));
return myClass;
}
然后,您需要做的就是让每个线程MyClass2
使用threadLocal.get()
方法,并且您将拥有当前线程MyClass2
。这消除了不同线程更改值的担心,因为每个线程都有自己的。
我希望这有点帮助,请告诉我,如果我误解了你的问题:)
答案 3 :(得分:1)
在多线程的考试或面试问题中,这个实现可能是一个很好的例子!
在回答您的问题之前,有三点:
我。)无法扩展final
类。它根本不暗示类的实例是不可变的。所以这是多线程问题的红鲱鱼。
II。) 。通常,您将使用关键字CreateMyClass1/2
具有相同的实现,违反了我最喜欢的编码原则不要重复自己(DRY)synchronized
来阻止方法被不同的线程同时调用,然后您只需要一个方法。 synchronized关键字阻止_class2
变量被两个线程交错:
public static synchronized MyClass CreateMyClass(InputStream arg0 , ArrayList<MyClass3> class3) throws Exception{
MyClass myClass = new MyClass();
_class2 = new Class2(arg0 , class3);
return myClass;
}
III。)更严格的方法是在类变量周围使用synchronized块,因为你需要保护的唯一共享变量是静态_class2变量,它是一个类资源。局部变量myClass不在单独的线程之间共享,因此您不必担心它是交错的:
public static MyClass CreateMyClass(InputStream arg0 , ArrayList<MyClass3> class3) throws Exception
{
MyClass myClass = new MyClass();
synchronized (MyClass.class){
_class2 = new Class2(arg0 , class3);
};
return myClass;
}
现在你的问题:
1)
除非两个线程可以更改共享可变对象的状态,否则我不需要担心线程安全
因此,当使用MyClass时,thread1可以调用CreateMyClass1和thread2 调用CreateMyClass2,这意味着_class2变量将是 由thread2改变,这是我想要避免的。将制作_class2 因为挥发性阻止这个?如果是,我不确定静态不稳定 将由JVM解释?这足以使MyClass成为现实 线程安全?
public final class MyClass
{
private static volatile MyClass2 _class2;
public static MyClass CreateMyClass(String arg0 , ArrayList<MyClass3> class3) throws Exception
{
MyClass myClass = new MyClass();
_class2 = new Class2(arg0 , class3);
return myClass;
}
}
但是,随后使用_class2的非可变方法的任何其他MyClass方法都不是线程安全的。由于volatile只同步_class2的赋值。
2)
不,myClass是一个局部变量。