![](/img/trans.png)
[英]Why is there no synchronized keyword used in Java lock implementations?
[英]Why count less even used synchronized keyword?
我開始學習一些使用的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() ?
這很簡單。 通過調用getCount + 1來調用setCount方法。在輸入方法之前,運行時會計算getCount(synchronized),但是在離開getCount並輸入setCount並且其他線程可以輸入調用getCount時,您不會保持鎖定。 因此,每隔兩次(或更多,取決於您創建的線程數)線程將在getCount中具有相同的值。 想象一下,線程A在getCount中輸入並接收值1。 運行時產生它執行到胎面B,它調用getCount並接收相同的值1.線程B將值設置為1並再次運行50次,因此在該階段您的計數將為50。 運行時產生執行到線程A,線程A調用setCount為1(記住它沒有設法調用setCount並產生它的exec)。 現在A將值設置為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)各自同步,但整個塊 不同步。 因此它們仍然可以交錯,這意味着線程A可以在線程B執行線程(a)之后但在它完成/進入語句(c) 之前進入/執行語句(a)。 當發生這種情況時,線程(a)和(b)將具有相同的oldCount並因此錯過一個增量。
2。
join()用於確保在打印之前線程A和線程B都完成。 你得到一個較小的數量的原因,因為當你打印你的結果線程A和B可能還沒有完成運行。 換句話說,即使你沒有join()完美地同步,你仍然會有一個遠小於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()
操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.