Java线程安全适用于类的所有实例还是仅适用于共享实例?
我试图确定我是否需要在我编写的几个关键类中担心线程安全。我已经阅读了几篇文章/现有的SO问题,我一直看到相同的、反复出现的线程安全定义: 线程安全意味着一个对象或类的字段始终保持有效状态,正如其他对象和类所观察到的那样,,即使多个线程同时使用。 嗯。我有点明白了。但我在这里遗漏了一个很大的谜题: 只有当一个类的同一个实例被多个线程使用时,或者只有当任何2个以上的实例被使用时,线程安全才会起作用吗?Java线程安全适用于类的所有实例还是仅适用于共享实例?,java,multithreading,concurrency,thread-safety,static-methods,Java,Multithreading,Concurrency,Thread Safety,Static Methods,我试图确定我是否需要在我编写的几个关键类中担心线程安全。我已经阅读了几篇文章/现有的SO问题,我一直看到相同的、反复出现的线程安全定义: 线程安全意味着一个对象或类的字段始终保持有效状态,正如其他对象和类所观察到的那样,,即使多个线程同时使用。 嗯。我有点明白了。但我在这里遗漏了一个很大的谜题: 只有当一个类的同一个实例被多个线程使用时,或者只有当任何2个以上的实例被使用时,线程安全才会起作用吗? 示例#1: 假设我有一个Dog类,它不包含静态方法或字段,假设我有5个不同的Dog实例,它们在5
示例#1: 假设我有一个
Dog
类,它不包含静态方法或字段,假设我有5个不同的Dog
实例,它们在5个不同的线程中运行。我需要关注线程安全吗?我会说“不”,因为没有静态字段/方法,每个线程都有自己的Dog
实例,其状态独立于其他4个实例(1)这是正确的吗?如果没有,原因是什么?
示例2: 现在让我们假设我向
Dog
添加了一个静态方法:
public void woof() {
this.isWoofing = true;
}
public static void makeWoof(Dog dog) {
dog.woof();
}
我现在需要关注线程安全吗?每个线程都有自己的狗的实例
,但现在它们共享相同的静态makeWoof()
方法,该方法更改它所操作的狗的状态。我还是说“不”(2)这是正确的吗?如果没有,原因是什么?
给出这两个例子,在我看来,当多个线程在同一个类实例上运行时,线程安全只是一个问题。但是当你给每个线程一个自己的实例时,我似乎可以对这个实例做任何我想做的事情,而不用担心其他线程内部发生了什么(3)这是正确的吗?若否,原因为何?他们有例外吗?提前谢谢 您关于线程安全性的假设是正确的。它归结为同一个实例上的字段/状态被多个线程修改
在您的第一个问题中,所有线程都操作自己的Dog
实例,因此该方法是线程安全的
在第二个问题中,实例被传递到静态方法中,因此您可以再次使用该类的不同实例。如果Dog
类包含静态字段,并且静态方法操作了那些不会是线程安全的字段,但是非最终静态字段永远不会是线程安全的。这与共享实例无关。这是关于共享状态的
考虑到这一点:
1:正确-如果您的线程不在共享状态下运行,那么它们本质上是线程安全的
2:不正确(sorta):-在这个特定静态方法的特定示例中,没有触及共享状态,但是静态方法可以创建/管理共享状态
3:见1和2
例如,让我们在OP的Dog
类中引入一点共享状态:
static Integer numberOfBarksToday=0;
因为它是静态的,所以是共享的。所以现在,静态方法(OP的makeWoof
方法的修改版本)可以处理:
public static void makeWoof(Dog dog) {
dog.woof();
synchronized(Dog.numberOfBarksToday) {
Dog.numberOfBarksToday++;
}
}
我刚刚意识到,当我创建上述示例时,出于习惯,我同步了访问。通过这种同步,这种特定的访问是线程安全的(当然,对numberOfBarksToday的所有其他访问也必须同步)
如果没有同步,如果多个线程调用此方法,您将倾向于低估当前的巴克数:
T0)Barkstoday的编号=0;
T1)线程A检查树皮的数量(第一部分++
),得到0。
T2)线程B检查树皮的数量,得到0。
T3)将一组树皮穿至1
T4)螺纹B将树皮数设置为1
这并不考虑在共享对象中,赋值方法是否是原子的
同步阻止了上述所有操作,并引入了内存屏障,以便所有线程都能看到numberOfBarksToday的相同值。(1)是
(2) 是,如果该方法不在任何非最终静态字段上运行。不,否则
(3) 是的,有关例外情况,请参见(2)。示例#1-您是正确的
如果我们可以证明某个对象对于单个线程来说是唯一可见的,那么它就被称为线程受限对象。线程受限对象不存在线程安全问题。。。而您的狗
实例是线程限制的
例子2——你是对的
您使用的是static
方法,这一事实在这里没有任何改变。给定您描述的示例,Dog
实例仍然是线程限制的
示例#3-假设所考虑的所有对象都是线程限制的,没有例外
请注意,如果将示例2中的woof
方法更改为使用某些共享状态(例如静态
变量),则线程安全可能会成为一个问题
另一件需要注意的事情是,可能很难知道实例是否以及何时将被线程限制。有两种应对策略:
- 通过使相关方法线程安全,您可以使
Dog
类线程安全。这意味着您不需要分析使用模式,但您的应用程序最终可能会执行不必要的同步
- 如果需要,您可以将其交给使用
Dog
类进行外部同步的类
您询问了静态方法如何可能引入线程问题。下面我提供了一个带有注释的代码示例。请注意,没有任何东西可以阻止非静态方法
public class Dog
{
private static boolean isWagging;
private boolean isWoofing;
public void woof()
{
this.isWoofing = true;
}
public static void wag()
{
isWagging = true;
}
public static void makeWoof(Dog dog)
{
/**
* Thread safety: woof() only modifies instance variables of 'Dog'.
* So if no dog instances are shared between threads, then no
* instance variables of any Dog are shared between threads, and so
* (as long as we do not share any Dog across threads) then there is
* no concern about needing to control access to shared state. Note
* that woof() *could* be changed to also modify 'isWagging' which
* is a static variable, and thus not protected by the "Dog
* instances are not shared between threads" contract. There is no
* guarantee that just because a method is an "instance" method, it
* will not modify shared state. It is a good general practice for
* member methods to only modify member variables, but sometimes
* modifying shared state (e.g. a database) in a member method is
* somewhat unavoidable.
*/
dog.woof();
/**
* Thread safety: wag() is a static method that operates on a static
* variable. Instances of Dog do not get separate copies of static
* variables, as the nature of 'static' means that the variable is
* attached to the Dog _class_ itself, not to _instances_ of the Dog
* class. You could say that, in the current implementation, if
* *any* dog wags, then all dogs will be marked as wagging, which is
* probably not what we want. Additionally, since there is no
* synchronization mechanism being used, there is no guarantee that
* other threads will see that that the value of 'isWagging' has
* been updated.
*/
wag();
/**
* Additional note: Java makes the static/non-static issue confusing
* by allowing the following syntax to compile. The following syntax
* *might* lead some programmers to believe that some dogs can be
* wagging while others are not. Most compilers will warn you about
* this syntax because it misleadingly makes it appear as if
* isWagging is an instance variable, and wag is an instance method
* (which is not the case.)
*/
if (!dog.isWagging)
{
dog.wag();
}
/**
* To be less ambiguous, you should really write the above code as:
*/
if (!isWagging)
{
wag();
}
/**
* Or even better: do not use any non-final static variables in your
* program at all.
*/
}
}