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));
}
}
});
}
当我运行上面的简单存款函数时,我预计两个线程不应该同步在同步块中输入。但是操作顺序如下:
输出如下:
------------------
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
正如我们可以看到在同步块中同时输入两个线程并访问了不期望的平衡对象。 我在这做错了什么?我是多线程的新手。 提前谢谢。
答案 0 :(得分:10)
Integer
(与所有原始包装类一样)是 immutable 。每当您向其添加号码时,您实际上都会将该字段设置为新实例:
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.getAndIncrement
或AtomicInteger.incrementAndGet
)。
请注意,您可能应该引用final
上同步的内容,以避免意外地将其设置为某个不同的值并再次打破互斥。
有关详细信息,请参阅this question。
答案 1 :(得分:0)
正如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();
}