【问题标题】:Synchronized() block is not working as expectedSynchronized() 块未按预期工作
【发布时间】:2017-02-19 17:39:23
【问题描述】:
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));
            }
        }
    });
}

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

  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

我们可以看到两个线程同时进入同步块并访问了不期望的平衡对象。 我在这里做错了什么?我是多线程的新手。 提前致谢。

【问题讨论】:

  • balance 每次都是不同的对象。没有相互排斥。
  • 请注意,当您使用+ 构建字符串时,您不需要调用Integer.toString(amt):字符串连接会自动为您执行此操作。

标签: java multithreading


【解决方案1】:

Integer(与所有原始包装类一样)是不可变的。每次“添加一个数字”时,实际上是在将该字段设置为一个新实例:

balance += amt;

实际评估如下:

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

所以您每次都在不同的对象上同步(除非amt 恰好为零,而balance.intValue()in the range cached by your JVM's implementation of Integer.valueOf)。

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


请注意,您可能应该引用您在 final 上同步的内容,以避免意外将其设置为不同的值并再次破坏互斥。

更多详情请见this question

【讨论】:

  • 谢谢!我将尝试了解 AtomicInteger。你能解释一下你的笔记部分吗?
  • 很好的解释。
【解决方案2】:

正如Andy Turner 所指出的,这是因为同步是在synchronized 块内修改的不可变实例上完成的。

解决方案 1: 按照 Andy 的建议,使用 AtomicInteger

解决方案 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();
}

【讨论】:

    猜你喜欢
    • 2020-05-31
    • 2014-08-26
    • 2018-11-24
    • 2016-01-23
    • 2021-09-21
    • 2014-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多