[英]Thread safety in java for static methods
我有以下代码
//编辑: - 使用@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设为volatile会阻止这种情况吗? 如果是,我不确定JVM将如何解释静态volatile? 这足以使MyClass线程安全吗?
在两个静态方法中返回MyClass类的对象是否会导致任何违反线程安全的行为?
因此,当使用MyClass时,thread1可以调用CreateMyClass1,而thread2调用CreateMyClass2,这意味着_class2变量将被thread2更改,这是我想要避免的。
静态变量在线程之间有效共享。 但是,其他线程不会自动看到一个线程的更改。 将变量声明为volatile
确保可见性。
我不确定JVM如何解释静态volatile?
这里有一个来自JVM规范的粘贴,可以解释所有内容并确认您的理解。
ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.
在各自的顺序中,参数是标志名称,值,解释。
在两个静态方法中返回MyClass类的对象是否会导致任何违反线程安全的行为?
根本不在方法范围中实例化对象,它将作为任何其他对象处理。
有关线程安全的更多详细信息可能有用
synchronized
添加到静态方法时,将锁定Class
对象。 static表示与包含类的实例无关。 这意味着所有对象(和静态方法)共享同一个变量。
volatile只是意味着其他线程可以在没有警告的情况下更改该值。
将变量声明为volatile(无论是否为静态)表明该变量将被多个线程频繁访问。 在Java中,这可以归结为指示线程无法缓存变量的值,但必须在变异后立即回写,以便其他线程看到更改。 (默认情况下,Java中的线程可以自由缓存变量)。
所以这两个修饰符的效果是完全正交的。 您可以将变量初始化为静态volatile
考虑各种关键词的含义/含义;
volatile
- 它保证读取该字段的任何线程都将看到最新/最近写入的值(这仅在您需要共享可变数据时才有用)
synchronized
- 获取对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
。 这消除了由于每个线程都有自己的不同线程而改变值的担忧。
我希望这有点帮助,请告诉我,如果我误解了你的问题:)
在多线程的考试或面试问题中,这种实现可能是一个很好的例子!
在回答您的问题之前,这里有三点:
I.) 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设为volatile会阻止这种情况吗? 如果是,我不确定JVM将如何解释静态volatile? 这足以使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是一个局部变量。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.