java 線程個人總結

[b]關於原子[/b]
嗯,爲什麼要加鎖?相信有點基礎的童鞋都很清楚。在多線程情況下調用方法主要會出現兩類問題 --原子性和非原子性!那他們各自會在神馬情況下出現捏?

舉一個《thinking in java》第四版中的例子。有一個EvenGenerator類,它的next()方法用來生成偶數。如下:

public class EvenGenerator {

private int currentValue = 0;
private boolean cancled = false;

public int next() {
++currentValue; //危險!
++currentValue;
return currentValue;
}

public boolean isCancled() {
return cancled;
}
public void cancle() {
cancled = true;
}
}


另外有一個EvenChecker類,用來不斷地檢驗EvenGenerator的next()方法產生的是不是一個偶數,它實現了Runnable接口。


public class EvenChecker implements Runnable {

private EvenGenerator generator;

public EvenChecker(EvenGenerator generator) {
this.generator = generator;
}

@Override
public void run() {
int nextValue;
while(!generator.isCancled()) {
nextValue = generator.next();
if(nextValue % 2 != 0) {
System.out.println(nextValue + "不是一個偶數!");
generator.cancle();
}
}
}
}


然後創建兩個EvenChecker來併發地對同一個EvenGenerator對象產生的數字進行檢驗。


public class Test {

public static void main(String[] args) {
EvenGenerator generator = new EvenGenerator();
Thread t1 = new Thread(new EvenChecker(generator));
Thread t2 = new Thread(new EvenChecker(generator));

t1.start();
t2.start();
}
}


顯然,在一般情況下,EvenGenerator的next()方法產生的數字肯定是一個偶數,因爲在方法體裏進行兩次”++currentValue”的操作。但是運行這個程序,輸出的結果竟然像下面這樣(並不是每次都是這個一樣的結果,但是程序總會因這樣的情況而終止):


849701不是一個偶數!


錯誤出在哪裏呢?程序中有“危險”註釋的哪一行便可能引發潛在的錯誤。因爲很可能某個線程在執行完這一行只進行了一次遞增之後,CPU時間片被另外一個線程奪去,於是就生產出了奇數。

原子性(atomicity)
具有原子性的操作被稱爲原子操作。原子操作在操作完畢之前不會線程調度器中斷。在Java中,對除了long和double之外的基本類型的簡單操作都具有原子性。簡單操作就是賦值或者return。比如”a = 1;”和 “return a;”這樣的操作都具有原子性。但是在Java中,上面例子中的類似”++currentValue;”這樣的操作不具有原子性,所以如果add方法不是同步的就會出現難以預料的結果。在某些JVM中”++currentValue;”可能要經過這樣四個步驟:
1.從內存得到currentValue的值
2.第一次+currentValue
3.第二次+currentValue
4.把currentValue寫回內存
上面會出現奇數的情況就是第一次+currentValue完成的時候,還沒來得及往內存中寫數據。CPU的時間片被另一個線程佔用,當那個線程使用完CPU切換回來的時候再來運行+currentValue,就會出現奇數了!

解決以上問題的方法無疑就是同步

[b]關於synchronized[/b]
1.方法上加synchronized 是給整個類加鎖。synchronized(Object){}是給object對象加鎖。相對而言各有各的用處,但是,不到迫不得已堅決不用synchronized加鎖!

步的代價:首先,同步化的方法有些額外的成本。進入同步化方法的程序會查詢鎖等性能上的損耗。其次,同步化方法會強制線程隊列排隊等着執行方法。最後,同步化很可能會導致死鎖!!!!!!!!!!!!!!!!!!

[color=red]同步的死鎖[/color]
在java中,死鎖是一件很恐怖的事情!因爲java根本沒有處理死鎖的機制,他甚至不會知道死鎖的發生!

下面來看一下可以如何導致死鎖
head firs java 中的說法

兩個線程和兩個對象
線程A foo
線程B bar
1.線程A進入對foo對象設定同步化的方法
2.線程B進入對bar對象設定同步化的方法,線程B接着嘗試要進入A正在執行的方法。所以B一直等着。
3.線程A醒來嘗試調用線程B正在執行的方法,但拿不到鑰匙,所以只好等着
A一直等着B的bar鑰匙,B卻也在等着A的foo鑰匙。兩個線程就這樣僵持着!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章