为什么这个类不是线程安全的?

时间:2015-05-13 07:38:12

标签: java multithreading thread-safety

class ThreadSafeClass extends Thread
{
     private static int count = 0;

     public synchronized static void increment()
     {
         count++;
     }

     public synchronized void decrement()
     {
         count--;
     }
}

有人可以解释为什么上面的类不是线程安全的吗?

7 个答案:

答案 0 :(得分:133)

由于increment方法为static,因此它将在ThreadSafeClass的类对象上进行同步。 decrement方法不是静态的,并且会在用于调用它的实例上进行同步。即,它们将在不同的对象上同步,因此两个不同的线程可以同时执行这些方法。由于++--操作不是原子操作,因此类不是线程安全的。

此外,由于countstatic,因此从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上的

fun1fun2在实例对象级别上同步。 synchronized上的fun4在类对象级别上同步。这意味着:

  1. 当2个线程同时呼叫a1.fun1()时,后一个呼叫将被阻止。
  2. 当线程1同时呼叫a1.fun1()并且线程2呼叫a1.fun2()时,后一个呼叫将被阻止。
  3. 当线程1同时调用a1.fun1()并且线程2调用a1.fun3()时,没有阻塞,这两个方法将同时执行。
  4. 当线程1呼叫A.fun4()时,如果其他线程同时呼叫A.fun4()A.fun5(),则后来的呼叫将被阻止,因为synchronized上的fun4是班级。
  5. 当线程1调用A.fun4()时,线程2同时调用a1.fun1(),没有阻塞,这两种方法将同时执行。

答案 4 :(得分:6)

  1. decrement锁定了与increment不同的内容,因此它们不会阻止彼此运行。
  2. 在一个实例上调用decrement会锁定另一个在另一个实例上调用decrement的东西,但它们会影响同样的事情。
  3. 第一个意味着重叠调用incrementdecrement可能会导致取消(正确),增量或减量。

    第二个意味着在不同实例上对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();
    }

}