[英]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));
}
}
});
}
当我运行上面的简单存款函数时,我希望两个线程不应该在synchronized块中同时进入。 但是操作顺序如下:
输出如下:
------------------
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
我们可以看到在同步块中同时输入两个线程并访问了不期望的平衡对象。 我在这做错了什么? 我是多线程的新手。 提前致谢。
Integer
(与所有原始包装类一样)是不可变的 。 每次“向其添加数字”时,您实际上都是将字段设置为新实例:
balance += amt;
实际上评估如下:
balance = Integer.valueOf(balance.intValue() + amt);
所以,你在不同的对象,每次同步(除非amt
恰好是零, balance.intValue()
是由你的JVM的执行缓存范围Integer.valueOf
)。
您需要一个可以同步的固定值。 您可以使用具有可变值的固定引用,例如长度为1的Integer[]
或AtomicInteger
(尽管AtomicInteger
上的同步对我来说总觉得有点不对 - 但实际上,您不需要使用同步,因为你可以使用AtomicInteger.getAndIncrement
或AtomicInteger.incrementAndGet
)。
请注意,您可能应该引用final
同步的事物,以避免意外地将其设置为某个不同的值并再次打破互斥。
有关详细信息,请参阅此问题 。
正如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();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.