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

das*_*der 94 java multithreading thread-safety

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

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

     public synchronized void decrement()
     {
         count--;
     }
}
Run Code Online (Sandbox Code Playgroud)

谁能解释为什么上面的类不是线程安全的?

K E*_*son 134

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

此外,由于count就是static,从修改它decrement是一种同步的实例方法是不安全的,因为它可以在不同的情况下被调用和修改count同时的方式.

  • 你可以补充一下,因为`count`是`static`,有一个实例方法`decrement()`是错误的,即使没有`static``inset()`方法,因为两个线程可以调用`decrement()`在不同的实例上同时修改同一个计数器. (12认同)

Sli*_*imu 23

您有两个同步方法,但其中一个是静态的,另一个不是.访问同步方法时,根据其类型(静态或非静态),将锁定另一个对象.对于静态方法,锁定将放在Class对象上,而对于非静态块,锁定将放在运行该方法的类的实例上.因为您有两个不同的锁定对象,所以可以有两个线程同时修改同一个对象.


Jea*_*ard 14

谁能解释为什么上面的类不是线程安全的?

  • increment 静态,同步将在类本身上完成.
  • decrement如果不是静态的,则会在对象实例化上完成同步,但这不会像count静态那样保护任何内容.

我想补充一点来声明一个线程安全的计数器,我相信最简单的方法是使用AtomicInteger而不是原始的int.

让我将您重定向到java.util.concurrent.atomicpackage-info.


cod*_*erz 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();
Run Code Online (Sandbox Code Playgroud)

synchronizedon fun1fun2在实例对象级别上同步.synchronizedon 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()同时调用,后面的调用将被阻止,因为synchronizedon fun4是类级别.
  5. 当线程1调用时A.fun4(),线程2同时调用a1.fun1(),没有阻塞,这两个方法将同时执行.


Jon*_*nna 6

  1. decrement是锁定不同的东西,increment所以他们不会阻止彼此运行.
  2. 调用decrement一个实例是锁定一个不同的东西来调用decrement另一个实例,但它们正在影响同样的事情.

第一个意味着重叠调用incrementdecrement可能导致取消(正确),增量或减量.

第二个意味着decrement对不同实例的两次重叠调用可能导致双重递减(正确)或单次递减.