为什么这个类不是线程安全的?
class ThreadSafeClass extends Thread { private static int count = 0; public synchronized static void increment() { count++; } public synchronized void decrement() { count--; } }
任何人都可以解释为什么以上类不是线程安全的?
由于increment
方法是static
,它将同步在ThreadSafeClass
的类对象上。 decrement
方法不是静态的,将在用于调用它的实例上同步。 也就是说,它们将在不同的对象上进行同步,因此两个不同的线程可以同时执行这些方法。 由于++
和--
操作不是primefaces的,所以类不是线程安全的。
另外,由于count
是static
,所以从同步实例方法的decrement
修改它是不安全的,因为它可以在不同的实例上调用,并以这种方式同时修改count
。
你有两个同步的方法,但其中一个是静态的,另一个不是。 当访问同步方法时,根据types(静态或非静态),不同的对象将被locking。 对于一个静态方法来说,一个锁将被放在Class对象上,而对于非静态块来说,一个锁会被放在运行该方法的类的实例上。 因为你有两个不同的locking对象,所以你可以有两个线程同时修改同一个对象。
任何人都可以解释为什么以上类不是线程安全的?
-
increment
是静态的,同步将在类本身完成。 -
decrement
不是静态的,同步将在对象实例化上完成,但是由于count
是静态的,所以不能保证任何东西。
我想补充说,声明一个线程安全的计数器,我相信最简单的方法是使用AtomicInteger
而不是原始的int。
让我redirect到java.util.concurrent.atomic
包信息。
-
decrement
locking在不同的东西上increment
所以它们不会阻止对方运行。 - 在一个实例上调用
decrement
是locking在另一个事件上调用另一个实例上的decrement
,但它们影响同一件事情。
第一种意思是重叠调用increment
和decrement
可能会导致取消(正确),增量或减量。
第二个意思是两个重叠的调用在不同的实例上decrement
会导致双重递减(正确)或单个递减。
其他人的回答很好解释了原因。 我只是添加一些总结synchronized
:
public class A { public synchronized void fun1() {} public synchronized void fun2() {} public void fun3() {} public static synchronized void fun4() {} public static void fun5() {} } A a1 = new A();
synchronized
fun1
和fun2
在实例对象级同步。 synchronized
fun4
在类对象级同步。 意思是:
- 当两个线程同时调用
a1.fun1()
时,后面的调用将被阻塞。 - 当线程1调用
a1.fun1()
和线程2同时调用a1.fun2()
时,后面的调用将被阻塞。 - 当线程1调用
a1.fun1()
和线程2同时调用a1.fun3()
时,不阻塞,同时执行2个方法。 - 当线程1调用
A.fun4()
,如果其他线程同时调用A.fun4()
或A.fun5()
,后面的调用将被阻塞,因为fun4
上的synchronized
是类层次的。 - 当线程1调用
A.fun4()
,线程2同时调用a1.fun1()
,不阻塞,同时执行2个方法。
由于两个不同的方法,一个是实例级别,另一个是类级别,所以你需要locking2个不同的对象,使其成为ThreadSafe
正如其他答案中所解释的,你的代码不是线程安全的,因为静态方法increment()
locking类监视器和非静态方法decrement()
锁对象监视器。
对于这个代码示例,没有synchronzed
关键字用法存在更好的解决scheme 你必须使用AtomicInteger来实现线程安全。
线程安全使用AtomicInteger
:
import java.util.concurrent.atomic.AtomicInteger; class ThreadSafeClass extends Thread { private static AtomicInteger count = new AtomicInteger(0); public static void increment() { count.incrementAndGet(); } public static void decrement() { count.decrementAndGet(); } public static int value() { return count.get(); } }