Java线程安全是否适用于类的所有实例或仅适用于共享实例?

8 java concurrency multithreading static-methods thread-safety

我正在尝试确定是否需要担心我编写的几个关键类中的线程安全性.我已经阅读了几篇文章/现有的SO问题,我一直看到线程安全的反复定义:

线程安全意味着对象或类的字段始终保持有效状态,如其他对象和类所观察到的,即使由多个线程同时使用也是如此.

好.我有点儿了.但是我在这里缺少一个很大的难题:

当多个线程使用一个类实例时,或者只是在使用任何2个以上的实例时,线程安全是否仅起作用?


示例#1:

假设我有一个Dog不包含静态方法或字段的类,并且假设我有5 Dog个不同线程中正在操作的5个不同实例.我是否需要关注线程安全?我会说"不",因为没有静态字段/方法,并且每个线程都有自己的实例,Dog其状态独立于其他4个实例而存在.(1)这是对的吗?如果没有,为什么?


示例#2:

现在让我们说我添加一个静态方法Dog:

public void woof() {
    this.isWoofing = true;
}

public static void makeWoof(Dog dog) {
    dog.woof();
}
Run Code Online (Sandbox Code Playgroud)

我现在需要关注线程安全吗?每个线程都有自己的a实例Dog,但现在它们共享相同的静态makeWoof()方法,这会改变Dog它正在运行的状态.我仍然说"不".(2)这是对的吗?如果没有,为什么?

鉴于这两个例子,在我看来,当多个线程在一个类的同一个实例上运行时,线程安全只是一个问题.但是你给每个线程自己的实例的那一刻,似乎我可以做任何我想要的那个实例,而不用担心其他线程内部发生了什么.(3)这是对的吗?如果没有,为什么?它们有什么例外吗?提前致谢!

Kev*_*sox 5

您对线程安全的假设是正确的.它归结instance为由多个线程修改的字段/状态.

在你的第一个问题中,线程都操纵它们自己的实例,Dog因此该方法是线程安全的.

在第二个问题中,实例被传递给静态方法,因此您再次使用该类的不同实例.如果Dog类包含静态字段,静态方法操作那些不是线程安全的字段,但非最终静态字段实际上从不是线程安全的.


CPe*_*ins 5

它并不是真正的共享实例.这是关于共享的状态

考虑到这一点:

1:正确 - 如果您的线程不在共享状态下运行,它们本质上是线程安全的.

2:不正确(sorta): - 在此特定静态方法的特定示例中,未触及共享状态,但静态方法可以创建/操作共享状态.

3:见1和2

举个例子,让我们在OP的Dog类中引入一小部分共享状态:

    static Integer numberOfBarksToday=0; 
Run Code Online (Sandbox Code Playgroud)

因为它是静态的,所以它是共享的.所以现在,静态方法(OP makeWoof方法的修改版本)可以操作:

    public static void makeWoof(Dog dog) {
        dog.woof();
        synchronized(Dog.numberOfBarksToday) {
            Dog.numberOfBarksToday++;
        }
    }       
Run Code Online (Sandbox Code Playgroud)

我刚刚意识到,当我创建上面的示例时,我将访问与习惯同步.通过该同步,此特定访问是线程安全的(当然,对numberOfBarksToday的所有其他访问也必须同步).

如果没有同步,多个线程调用此方法,您将倾向于低估今天的树皮数量:T0)numberOfBarksToday = 0; T1)线程A检查树皮数量(第一部分++),得到0. T2)线程B检查树皮数量,得到0. T3)线程A设置树皮数量为1 T4)线程B设置树皮数量为1

而且没有考虑是否在共享对象中,赋值方法是原子的.

同步会阻止上述所有情况,并引入内存屏障,以便所有线程看到numberOfBarksToday的相同值.