简体   繁体   English

Synchronized()块未按预期工作

[英]Synchronized() block is not working as expected

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));
            }
        }
    });
}

When I run the above simple deposit function, I expect that two threads should not enter at same time in synchronized block. 当我运行上面的简单存款函数时,我希望两个线程不应该在synchronized块中同时进入。 But with operation Sequence as below: 但是操作顺序如下:

  1. Depo100 Depo100
  2. Depo200 Depo200
  3. Depo700 Depo700

Output is as below: 输出如下:

------------------
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

As we can see two threads entered at same time in the synchronized block and accessed balance object which is NOT expected. 我们可以看到在同步块中同时输入两个线程并访问了不期望的平衡对象。 What I am doing wrong here ? 我在这做错了什么? I am newbie for Multithreading. 我是多线程的新手。 Thanks in advance. 提前致谢。

Integer (like all primitive wrapper classes) is immutable . Integer (与所有原始包装类一样)是不可变的 Every time you "add a number to it", you're actually setting the field to a new instance: 每次“向其添加数字”时,您实际上都是将字段设置为新实例:

balance += amt;

is actually evaluated as follows: 实际上评估如下:

balance = Integer.valueOf(balance.intValue() + amt);

So you're synchronizing on different objects each time (unless amt happens to be zero, and balance.intValue() is in the range cached by your JVM's implementation of Integer.valueOf ). 所以,你在不同的对象,每次同步(除非amt恰好是零, balance.intValue()由你的JVM的执行缓存范围Integer.valueOf )。

You need a fixed value that you can synchronize on. 您需要一个可以同步的固定值。 You can use a fixed reference with a mutable value, for example an Integer[] with length 1, or an AtomicInteger (although synchronizing on an AtomicInteger always feels a bit wrong to me - but actually, you don't need to use synchronization because you can use AtomicInteger.getAndIncrement or AtomicInteger.incrementAndGet ). 您可以使用具有可变值的固定引用,例如长度为1的Integer[]AtomicInteger (尽管AtomicInteger上的同步对我来说总觉得有点不对 - 但实际上,您不需要使用同步,因为你可以使用AtomicInteger.getAndIncrementAtomicInteger.incrementAndGet )。


Note that you should probably make the reference to the thing you synchronize on final , to avoid accidentally setting it to some different value and breaking the mutual exclusion again. 请注意,您可能应该引用final同步的事物,以避免意外地将其设置为某个不同的值并再次打破互斥。

See this question for more details. 有关详细信息,请参阅此问题

As Andy Turner pointed, it is because synchronization is done on an immutable instance which is being modified inside synchronized block. 正如Andy Turner指出的那样,这是因为同步是在一个不可变的实例上完成的,该实例在synchronized块中被修改。

Solution 1: As suggested by Andy, to use AtomicInteger . 解决方案1:正如Andy所建议的,使用AtomicInteger

Solution 2: To use synchronization on a dummy object like this: 解决方案2:在虚拟对象上使用同步,如下所示:

private static Object dummy = new Object();

private static Integer balance = 0;

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM