java 中静态方法的线程安全
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 解释?
这足以使 MyClass 线程安全吗?
是否在两个静态方法中返回 MyClass class 的对象
导致违反线程安全?
So when using MyClass , can thread1 call CreateMyClass1 and thread2
call CreateMyClass2 which will mean that the _class2 variable will be
changed by thread2 which is what i want to avoid.
静态变量在线程之间有效共享。但是,一个线程中的更改不会自动被其他线程看到。将变量声明为 volatile
保证可见性。
I am not sure how static volatile will be interpreted by the JVM?
这里有一段来自 JVM 规范的粘贴,解释了这一切并确认您的理解。
ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.
参数依次为flag name, value, interpretation.
Does returning an object of the MyClass class in both the static
methods cause any violation of thread-safety?
根本不在方法范围内实例化对象,它将像处理任何其他对象一样处理。
有关线程安全的更多详细信息可能有用
- 每个线程都有自己的堆栈。
- 局部变量被添加到堆栈并自动线程安全。
- 线程安全的问题是当你试图在它们之间共享数据时,这里不是这种情况。
- 如果你想在多个线程之间共享简单的值,你可能想看看 atomic variables 而不是使用 synchronized 关键字。
- 数据静态与否无所谓,只有共享才会出问题
- 将
synchronized
添加到静态方法时,Class
对象将被锁定。
static 表示不与包含 class 的实例相关联。这意味着您的所有对象(和静态方法)共享同一个变量。
volatile 只是意味着该值可能会在没有警告的情况下被其他线程更改。
将变量声明为 volatile(无论是否为静态)表明该变量将被多个线程频繁访问。在 Java 中,这归结为指示线程它们不能缓存变量的值,但必须在更改后立即写回,以便其他线程看到更改。 (Java 中的线程默认可以自由缓存变量)。
所以这两个修饰符的效果是完全正交的。您可以将变量初始化为 static volatile
考虑一下各种关键字do/mean;
volatile
- 它保证任何读取该字段的线程将看到最多的 up-to-date/recently 写入值(这仅在您有需要共享的可变数据时才有用)
synchronized
- 获取同步内容的锁,这样其他线程就无法执行任何操作,直到拥有锁的线程完成其需要的操作
鉴于您的 CreateMyClass1
和 CreateMyClass2
方法都将 _class2
重新分配给 MyClass2
的新实例,不同的线程可能会更改 _class2
.
如果您想为每个线程设置一个单独的值,您可以考虑查看 ThreadLocal
。也许使用静态 ThreadLocal
可能适合您的需要;
private static ThreadLocal<MyClass> threadLocal = new ThreadLocal<MyClass>();
这将取代 private static MyClass2 _class2;
然后在创建方法中你可以设置每个线程 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.) A final
class 无法扩展。它根本不意味着 class 的实例是不可变的。因此,就这里的多线程问题而言,这是一个转移注意力的问题。
II.) CreateMyClass1/2
具有相同的实现,违反了我最喜欢的编码原则不要重复自己 (DRY)。通常你会使用关键字synchronized
来防止一个方法被不同的线程同时调用那么你只需要一个方法。 synchronized 关键字防止 _class2
变量被两个线程交错:
public static synchronized MyClass CreateMyClass(InputStream arg0 , ArrayList<MyClass3> class3) 抛出异常{
MyClass myClass = new MyClass();<br>
_class2 = new Class2(arg0, class3);
return 我的班级;
}
III.) 更严格的方法是在 class 变量周围使用同步块,因为您需要保护的唯一共享变量是静态 _class2 变量,并且它是 class 资源。局部变量 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)
Unless two threads can change the state of a shared mutable object , i don't need to worry about thread safety
- 正确。
So when using MyClass , can thread1 call CreateMyClass1 and thread2
call CreateMyClass2 which will mean that the _class2 variable will be
changed by thread2 which is what i want to avoid. Will making _class2
as volatile prevent this? If yes , I am not sure how static volatile
will be interpreted by the JVM? Will this be enough to make MyClass
thread-safe?
- 静态 _class2 变量将被静态方法 CreateMyClass 的后续调用更改。这是 运行 代码的唯一合理结果。相反,如果您想避免交错分配 _class2 以防止它进入错误状态,因为该方法被不同的线程调用,那么是的,volatile 将解决这个问题,并修复您的问题多线程问题(就此片段而言),允许删除同步块:
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 是一个局部变量。
我有以下代码
//编辑:- 使用@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 解释? 这足以使 MyClass 线程安全吗?
是否在两个静态方法中返回 MyClass class 的对象 导致违反线程安全?
So when using MyClass , can thread1 call CreateMyClass1 and thread2 call CreateMyClass2 which will mean that the _class2 variable will be changed by thread2 which is what i want to avoid.
静态变量在线程之间有效共享。但是,一个线程中的更改不会自动被其他线程看到。将变量声明为 volatile
保证可见性。
I am not sure how static volatile will be interpreted by the JVM?
这里有一段来自 JVM 规范的粘贴,解释了这一切并确认您的理解。
ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.
参数依次为flag name, value, interpretation.
Does returning an object of the MyClass class in both the static methods cause any violation of thread-safety?
根本不在方法范围内实例化对象,它将像处理任何其他对象一样处理。
有关线程安全的更多详细信息可能有用
- 每个线程都有自己的堆栈。
- 局部变量被添加到堆栈并自动线程安全。
- 线程安全的问题是当你试图在它们之间共享数据时,这里不是这种情况。
- 如果你想在多个线程之间共享简单的值,你可能想看看 atomic variables 而不是使用 synchronized 关键字。
- 数据静态与否无所谓,只有共享才会出问题
- 将
synchronized
添加到静态方法时,Class
对象将被锁定。
static 表示不与包含 class 的实例相关联。这意味着您的所有对象(和静态方法)共享同一个变量。
volatile 只是意味着该值可能会在没有警告的情况下被其他线程更改。
将变量声明为 volatile(无论是否为静态)表明该变量将被多个线程频繁访问。在 Java 中,这归结为指示线程它们不能缓存变量的值,但必须在更改后立即写回,以便其他线程看到更改。 (Java 中的线程默认可以自由缓存变量)。
所以这两个修饰符的效果是完全正交的。您可以将变量初始化为 static volatile
考虑一下各种关键字do/mean;
volatile
- 它保证任何读取该字段的线程将看到最多的 up-to-date/recently 写入值(这仅在您有需要共享的可变数据时才有用)
synchronized
- 获取同步内容的锁,这样其他线程就无法执行任何操作,直到拥有锁的线程完成其需要的操作
鉴于您的 CreateMyClass1
和 CreateMyClass2
方法都将 _class2
重新分配给 MyClass2
的新实例,不同的线程可能会更改 _class2
.
如果您想为每个线程设置一个单独的值,您可以考虑查看 ThreadLocal
。也许使用静态 ThreadLocal
可能适合您的需要;
private static ThreadLocal<MyClass> threadLocal = new ThreadLocal<MyClass>();
这将取代 private static MyClass2 _class2;
然后在创建方法中你可以设置每个线程 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.) A final
class 无法扩展。它根本不意味着 class 的实例是不可变的。因此,就这里的多线程问题而言,这是一个转移注意力的问题。
II.) 。通常你会使用关键字CreateMyClass1/2
具有相同的实现,违反了我最喜欢的编码原则不要重复自己 (DRY)synchronized
来防止一个方法被不同的线程同时调用那么你只需要一个方法。 synchronized 关键字防止 _class2
变量被两个线程交错:
public static synchronized MyClass CreateMyClass(InputStream arg0 , ArrayList<MyClass3> class3) 抛出异常{
MyClass myClass = new MyClass();<br>
_class2 = new Class2(arg0, class3);
return 我的班级;
}
III.) 更严格的方法是在 class 变量周围使用同步块,因为您需要保护的唯一共享变量是静态 _class2 变量,并且它是 class 资源。局部变量 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)
Unless two threads can change the state of a shared mutable object , i don't need to worry about thread safety
- 正确。
So when using MyClass , can thread1 call CreateMyClass1 and thread2 call CreateMyClass2 which will mean that the _class2 variable will be changed by thread2 which is what i want to avoid. Will making _class2 as volatile prevent this? If yes , I am not sure how static volatile will be interpreted by the JVM? Will this be enough to make MyClass thread-safe?
- 静态 _class2 变量将被静态方法 CreateMyClass 的后续调用更改。这是 运行 代码的唯一合理结果。相反,如果您想避免交错分配 _class2 以防止它进入错误状态,因为该方法被不同的线程调用,那么是的,volatile 将解决这个问题,并修复您的问题多线程问题(就此片段而言),允许删除同步块:
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 是一个局部变量。