class ThreadSafeClass extends Thread
{
private static int count = 0;
public synchronized static void increment()
{
count++;
}
public synchronized void decrement()
{
count--;
}
}
有人可以解释为什么上面的类不是线程安全的吗?
答案 0 :(得分:133)
由于increment
方法为static
,因此它将在ThreadSafeClass
的类对象上进行同步。 decrement
方法不是静态的,并且会在用于调用它的实例上进行同步。即,它们将在不同的对象上同步,因此两个不同的线程可以同时执行这些方法。由于++
和--
操作不是原子操作,因此类不是线程安全的。
此外,由于count
为static
,因此从decrement
修改它是同步的实例方法是不安全的,因为它可以在不同的实例上调用同时修改count
。
答案 1 :(得分:23)
您有两个同步方法,但其中一个是静态的,另一个不是。当访问同步方法时,基于它的类型(静态或非静态),将锁定不同的对象。对于静态方法,锁定将放在Class对象上,而对于非静态块,锁定将放在运行该方法的类的实例上。因为您有两个不同的锁定对象,所以可以有两个线程同时修改同一个对象。
答案 2 :(得分:14)
有人可以解释为什么上面的类不是线程安全的吗?
increment
是静态的,同步将在类本身上完成。decrement
不是静态的,同步将在对象实例化上完成,但由于count
是静态的,因此不会保护任何内容。我想添加它来声明一个线程安全的计数器,我相信最简单的方法是使用AtomicInteger
而不是原始的int。
让我将您重定向到java.util.concurrent.atomic
package-info。
答案 3 :(得分:7)
其他'答案很好解释了原因。我只是添加一些内容来总结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()
时,后一个呼叫将被阻止。a1.fun1()
并且线程2呼叫a1.fun2()
时,后一个呼叫将被阻止。a1.fun1()
并且线程2调用a1.fun3()
时,没有阻塞,这两个方法将同时执行。A.fun4()
时,如果其他线程同时呼叫A.fun4()
或A.fun5()
,则后来的呼叫将被阻止,因为synchronized
上的fun4
是班级。A.fun4()
时,线程2同时调用a1.fun1()
,没有阻塞,这两种方法将同时执行。答案 4 :(得分:6)
decrement
锁定了与increment
不同的内容,因此它们不会阻止彼此运行。decrement
会锁定另一个在另一个实例上调用decrement
的东西,但它们会影响同样的事情。第一个意味着重叠调用increment
和decrement
可能会导致取消(正确),增量或减量。
第二个意味着在不同实例上对decrement
的两次重叠调用可能导致双重递减(正确)或单次递减。
答案 5 :(得分:4)
由于有两种不同的方法,一种是实例级别,另一种是类级别,所以你需要锁定2个不同的对象才能使它成为ThreadSafe
答案 6 :(得分:1)
正如其他答案中所解释的那样,您的代码是 而不是线程安全 ,因为静态方法increment()
会锁定类监视器和非静态方法decrement()
锁对象监视器。
对于此代码示例,在没有synchronzed
关键字用法的情况下存在更好的解决方案。
您必须使用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();
}
}