Synchronized()块未按预期工作

cra*_*ead 4 java multithreading

private static Integer balance=0;

public static void deposit(final int amt) {
    Thread t = new Thread(new Runnable() {
        public void run() {
            synchronized(balance) {
                System.out.println("Balance at start is: "+balance);        
                balance+=amt;
                System.out.println("deposited " + Integer.toString(amt) + " to funds. Now at " + Integer.toString(balance));
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

当我运行上面的简单存款函数时,我希望两个线程不应该在synchronized块中同时进入.但是操作顺序如下:

  1. Depo100
  2. Depo200
  3. Depo700

输出如下:

------------------
Balance at start is: 0
deposited 100 to funds. Now at 100
Balance at start is: 100
Balance at start is: 100
deposited 700 to funds. Now at 800
deposited 200 to funds. Now at 1000
Run Code Online (Sandbox Code Playgroud)

正如我们可以看到在同步块中同时输入两个线程并访问了不期望的余额对象.我在这做错了什么?我是多线程的新手.提前致谢.

And*_*ner 10

Integer(像所有原始包装类一样)是不可变的.每次"向其添加数字"时,您实际上都是将字段设置为新实例:

balance += amt;
Run Code Online (Sandbox Code Playgroud)

实际上评估如下:

balance = Integer.valueOf(balance.intValue() + amt);
Run Code Online (Sandbox Code Playgroud)

所以,你在不同的对象,每次同步(除非amt恰好是零,balance.intValue()由你的JVM的执行缓存的范围内Integer.valueOf).

您需要一个可以同步的固定值.您可以使用具有可变值的固定引用,例如Integer[]长度为1 的固定引用,或者AtomicInteger(虽然同步对AtomicInteger我总是感觉有点不对 - 但实际上,您不需要使用同步,因为您可以使用AtomicInteger.getAndIncrementAtomicInteger.incrementAndGet) .


请注意,您可能应该引用您同步的事物final,以避免意外地将其设置为某个不同的值并再次打破互斥.

有关详细信息,请参阅此问题.