为什么连使用synchronized关键字的次数都少呢?
-
13-11-2019 - |
题
我开始学习一些java并发概念并投入使用。但这段代码中有一段超出了我的理解范围。
public class Count {
private int count = 0;
public synchronized void setCount(int count) {
this.count = count;
}
public synchronized int getCount() {
return count;
}
}
class CountRunner extends Thread {
Count count;
public CountRunner(Count count) {
this.count = count;
}
public void run() {
for (int i = 1; i <= 1000; i++) {
count.setCount(count.getCount() + 1);
}
}
}
class TestCount {
public static void main(String[] args) throws Exception {
Count count = new Count();
CountRunner runnerA = new CountRunner(count);
CountRunner runnerB = new CountRunner(count);
runnerA.start();
runnerB.start();
runnerA.join(); //join statement here
runnerB.join();
System.out.println("count.getCount = " + count.getCount());
}
}
Question: 1. The result is a little less than 2000 for many times, why ? 2. if delete 2 join() statement, why count.getCount = 451,even less ? 3. i think there will be no effect of deleting the join() statements, because i already have Synchronized method to lock one object to one thread each time ? So, What's the point of using Synchronized and join() ?
解决方案
很简单。您调用setCount方法通过调用getCount + 1.在进入方法之前,运行时评估GetCount(同步),但在离开GetCount并输入SetCount时,您不会按住锁定,其他线程可以进入呼叫GetCount。所以每一个现在,然后是两个(或更多,具体取决于您创建的有多少线程)线程在getCount中具有相同的值。想象一下,线程进入并在getCount中接收值1。运行时会产生它对踩踏B调用GetCount并接收相同值1.线程B将值设置为1并再次运行,因此在该阶段将是50的值。运行时会产生对线程A的执行,其中包含1(请记住它不设)调用setCount并产生它的Exec)。现在,将值设置为1(错误)。
更改您运行的实现:
public void run() {
for (int i = 1; i <= 1000; i++) {
synchronized(count){
count.setCount(count.getCount() + 1);
}
}
}
. 其他提示
-
如果打破行
count.setcount(count.getcount()+ 1);
分为3个单独的线条,它将更清晰:
.final int oldCount = count.getCount(); // a final int newCount = oldCount + 1; // b count.setCount(newCount); // c
请注意,虽然语句(a)和(c)各自同步,但整个块 not 。因此,它们仍然可以交错,这意味着线程a可以在线程b执行线程(a)之后输入/执行语句(a),但在它完成/输入语句之前 。当发生线程(a)和(b)时,它将具有相同的 oldcount 并因此错过了一个增量。
2.
join()是在打印之前确保两个线程A和线程B完成。如果您打印结果时,您获得较小的计数的原因是尚未完成尚未完成运行的。换句话说,即使您完全同步,无需加入(),您仍然有一个小于2000的数字。
3。 请参阅回答2。
1)因为你没有正确锁定。你正在呼唤 getCount()
, ,锁定、获取值并解锁、递增并调用 setCount()
它会锁定、保存值并解锁。在某些情况下,两个线程都会调用 getCount()
, ,第一个踏板锁,获取值 x
并解锁。然后第二个线程获得锁,获得相同的值 x
并解锁。由于两个线程都会递增并随后保存相同的值,因此您将获得比预期更低的计数。
2) 没有 join()
你不会等待你的线程完成,你的主线程只会调用 getCount()
并在线程仍在运行时获取随机值。
3)由于其他线程没有持有锁 每时每刻 (如果你想让他们都运行,他们毕竟需要给彼此解锁时间),你需要 join()
仍然。
someThread.join()
会导致调用 Thread
等到 someThread
已完成。
如果您删除 join()
调用,然后是主要的 Thread
可能会打电话 getCount()
在计数结束之前,因为 someThread
可能仍在运行。
同步方法仅仅意味着对一个对象的多个同步调用 Object
不能在 同时.
一行答案是 count.setCount(count.getCount() + 1);
不是原子操作。
或者稍微不那么神谕,同时 setCount
和 getCount
是正确的线程安全和原子的,没有什么可以阻止另一个线程调用这些方法中的任何一个 之间 该线程的调用 setCount
和 getCount
. 。这将导致计数丢失。
避免丢失计数的一种方法是创建一个原子 increment()
手术。