1 wait方法:
該方法屬於Object的方法,wait方法的做用是使得當前調用wait方法所在部分(代碼塊)的線程中止執行,並釋放當前得到的調用wait所在的代碼塊的鎖,並在其餘線程調用notify或者notifyAll方法時恢復到競爭鎖狀態(一旦得到鎖就恢復執行)。
調用wait方法須要注意幾點:
第一點:wait被調用的時候必須在擁有鎖(即synchronized修飾的)的代碼塊中。
第二點:恢復執行後,從wait的下一條語句開始執行,於是wait方法老是應當在while循環中調用,以避免出現恢復執行後繼續執行的條件不知足卻繼續執行的狀況。
第三點:若wait方法參數中帶時間,則除了notify和notifyAll被調用能激活處於wait狀態(等待狀態)的線程進入鎖競爭外,在其餘線程中interrupt它或者參數時間到了以後,該線程也將被激活到競爭狀態。
第四點:wait方法被調用的線程必須得到以前執行到wait時釋放掉的鎖從新得到纔可以恢復執行。
2 notify方法和notifyAll方法:
notify方法通知調用了wait方法,可是還沒有激活的一個線程進入線程調度隊列(即進入鎖競爭),注意不是當即執行。而且具體是哪個線程不能保證。另一點就是被喚醒的這個線程必定是在等待wait所釋放的鎖。
notifyAll方法則喚醒全部調用了wait方法,還沒有激活的進程進入競爭隊列。
3 synchronized關鍵字:
第一點:synchronized用來標識一個
普通方法時,表示一個線程要執行該方法,必須取得該方法所在的
對象的鎖。
第二點:synchronized用來標識一個
靜態方法時,表示一個線程要執行該方法,必須得到該方法所在的
類的類鎖。
第三點:synchronized修飾一個代碼塊。相似這樣:synchronized(
obj) { //co
de.... }。表示一個線程要執行該代碼塊,必須得到
obj的鎖。這樣作的目的是減少鎖的粒度,保證當不一樣塊所需的鎖不衝突時不用對整個對象加鎖。利用零長度的byte數組對象作obj很是經濟。
4 atomic act
ion(原子操做):
在JAVA中,如下兩點操做是原子操做。可是c和c++中並不如此。
第一點:對引用變量和除了long和double以外的原始數據類型變量進行讀寫。
第二點:對全部聲明爲volatile的變量(包括long和double)的讀寫。
另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依賴於同步機制的線程安全的類和方法。
5 一個例子,該例子模仿多人存取同一個帳戶:
Account類:
package com.synchronize;
imp
ort java.util.HashMap;
imp
ort java.util.Iterator;
public class Account {
private static HashMap<String, Integer> m = new HashMap<String, Integer>();
private static long times = 0;
static {
m.put("ren", 1000);
}
public synchronized void save(String name, int num) {
long tempTime = times++;
System.out.println("第 " + tempTime + " 次存儲" + num + "以前" + name + "的餘額爲:" + m.get(name));
m.put(name, m.get(name) + num);
this.notify();
System.out.println("第 " + tempTime + " 次存儲" + num + "以後" + name + "的餘額爲:" + m.get(name));
}
public static int get(String name) {
return m.get(name);
}
/**
* 注意wait的用法,必須在loop中,必須在擁有鎖的代碼塊中。 前者是當被notify的時候要從新進行條件判斷,後者是爲了釋放鎖。
*
* @param name
* @param num
*/
public synchronized void load(String name, int num) {
long tempTime = times++;
System.out.println("第 " + tempTime + " 次提取" + num + "以前" + name + "的餘額爲:" + m.get(name));
try {
while (m.get(name) < num) {
System.out.println("第 " + tempTime + " 次提取" + "餘額" + m.get(name) + "不足,開始等待wait。");
this.wait();
System.out.println("第 " + tempTime + " 次提取操做被喚醒");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
m.put(name, m.get(name) - num);
System.out.println("第 " + tempTime + " 次提取" + num + "以後" + name + "的餘額爲:" + m.get(name));
}
}
User類:
package com.synchronize;
/**
* 這裏注意runnable接口的線程是怎麼實例化的。new Thread(new User())
* 這裏成功展現了多個用戶存取同一個帳戶的多線程實例,經過多線程同步,保證了安全的執行。
* @author abc
*
*/
public class User implements Runnable {
private static Account account = new Account();
private final int id;
User(int i){
id=i;
}
public void run() {
int tempMoney = 100;
account.load("ren", tempMoney);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.save("ren", 100);
System.out.println("線程"+id+"完畢========================================================");
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new User(i)).start();
}
}
}
後續:請額外關注 ThreadLocal、JDK 5 中增長的 Lock 接口。
參考資料:
1 JDK Document
2
http://java.sun.com/docs/books/tutorial/essential/concurrency/locksync.html
3
http://java.sun.com/docs/books/tutorial/essential/concurrency/atomic.html