java 中的不正确同步

Improper synchronization in java

所以,我正在尝试了解 java 线程和同步。下面这段代码没有正确同步,有人能解释一下为什么吗?

package threadPractice;

public class T2 extends Thread {
     public static int count=0;
     public T2( ) { }
     private synchronized void update( ) {
         int v = count;
         try {
             sleep(10);
         } catch (Exception e) { }
         v++;
         count = v;
     }
     public void run( ) {
     for (int i = 0; i < 1000; i++) {
     update( );
     }
     }
}
package threadPractice;

public class Main {
    public static void main(String args[]) throws Exception {
         T2 t1_1 = new T2( );
         T2 t1_2 = new T2( );
         t1_1.start( ); t1_2.start( );
         t1_1.join( ); t1_2.join( );
         System.out.println("T2.start, "+T2.count); 

    }
}

我的预期输出是 2000。我的实际输出是在 0 到 2000 之间。

嗯,因为你让两个不同的对象同步。如果你想要一把锁来保护一个字段(count),你需要使用一把锁。为每个线程提供自己的锁意味着任何线程都不知道其他线程何时实际使用锁。

首先,如果您的字段是 public,则没有任何内容受到保护。设为私有。

第二种在这种情况下使用静态方法来保护静态字段。

public class T2 extends Thread {
     private static int count=0;

     private static synchronized void update( ) {
         int v = count;
         try {
             sleep(10);
         } catch (Exception e) { }
         v++;
         count = v;
     }

     @Override
     public void run( ) {
       for (int i = 0; i < 1000; i++) {
       update( );
     }
     }
}

此外,synchronizedwait/notify 是具有许多尖角的低级基元(如您所见)。更好的选择是使用 API.

public class T2 extends Thread {
     private static final AtomicInteger count = new AtomicInteger();

     @Override
     public void run( ) {
       for (int i = 0; i < 1000; i++) {
         count.incrementAndGet();
       }
     }
}

如果您不想使同步方法成为静态的(例如,因为您正在访问实例变量),您可以在静态内部对象中持有锁。 T2 的所有不同实例都将使用相同的锁引用,如您在此处所见:

public class T2 extends Thread {
    public static int count = 0;
    private final static Object LOCK = new Object(); // Static lock

    public T2() {
    }

    private void update() {
        synchronized (LOCK) { // Every instance of T2 uses the same lock
            int v = count;
            try {
                sleep(10);
            } catch (Exception e) {
            }
            v++;
            count = v;
        }
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            update();
        }
    }
}