1 wait方法:
該方法屬于Object的方法,,wait方法的作用是使得當(dāng)前調(diào)用wait方法所在部分(代碼塊)的線程停止執(zhí)行,并釋放當(dāng)前獲得的調(diào)用wait所在的代碼塊的鎖,,并在其他線程調(diào)用notify或者notifyAll方法時(shí)恢復(fù)到競(jìng)爭(zhēng)鎖狀態(tài)(一旦獲得鎖就恢復(fù)執(zhí)行),。 調(diào)用wait方法需要注意幾點(diǎn): 第一點(diǎn):wait被調(diào)用的時(shí)候必須在擁有鎖(即synchronized修飾的)的代碼塊中。 第二點(diǎn):恢復(fù)執(zhí)行后,,從wait的下一條語(yǔ)句開(kāi)始執(zhí)行,,因而wait方法總是應(yīng)當(dāng)在while循環(huán)中調(diào)用,以免出現(xiàn)恢復(fù)執(zhí)行后繼續(xù)執(zhí)行的條件不滿足卻繼續(xù)執(zhí)行的情況,。 第三點(diǎn):若wait方法參數(shù)中帶時(shí)間,,則除了notify和notifyAll被調(diào)用能激活處于wait狀態(tài)(等待狀態(tài))的線程進(jìn)入鎖競(jìng)爭(zhēng)外,在其他線程中interrupt它或者參數(shù)時(shí)間到了之后,,該線程也將被激活到競(jìng)爭(zhēng)狀態(tài),。 第四點(diǎn):wait方法被調(diào)用的線程必須獲得之前執(zhí)行到wait時(shí)釋放掉的鎖重新獲得才能夠恢復(fù)執(zhí)行。 2 notify方法和notifyAll方法: notify方法通知調(diào)用了wait方法,,但是尚未激活的一個(gè)線程進(jìn)入線程調(diào)度隊(duì)列(即進(jìn)入鎖競(jìng)爭(zhēng)),,注意不是立即執(zhí)行。并且具體是哪一個(gè)線程不能保證,。另外一點(diǎn)就是被喚醒的這個(gè)線程一定是在等待wait所釋放的鎖,。 notifyAll方法則喚醒所有調(diào)用了wait方法,尚未激活的進(jìn)程進(jìn)入競(jìng)爭(zhēng)隊(duì)列,。 3 synchronized關(guān)鍵字: 第一點(diǎn):synchronized用來(lái)標(biāo)識(shí)一個(gè)普通方法時(shí),,表示一個(gè)線程要執(zhí)行該方法,,必須取得該方法所在的對(duì)象的鎖。 第二點(diǎn):synchronized用來(lái)標(biāo)識(shí)一個(gè)靜態(tài)方法時(shí),,表示一個(gè)線程要執(zhí)行該方法,,必須獲得該方法所在的類的類鎖。 第三點(diǎn):synchronized修飾一個(gè)代碼塊,。類似這樣:synchronized(obj) { //co 4 atomic act 在JAVA中,,以下兩點(diǎn)操作是原子操作。但是c和c++中并不如此,。 第一點(diǎn):對(duì)引用變量和除了long和double之外的原始數(shù)據(jù)類型變量進(jìn)行讀寫,。 第二點(diǎn):對(duì)所有聲明為volatile的變量(包括long和double)的讀寫。 另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依賴于同步機(jī)制的線程安全的類和方法,。 5 一個(gè)例子,,該例子模仿多人存取同一個(gè)賬戶: Account類: package com.synchronize; imp imp 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 + " 次存儲(chǔ)" + num + "之前" + name + "的余額為:" + m.get(name)); m.put(name, m.get(name) + num); this.notify(); System.out.println("第 " + tempTime + " 次存儲(chǔ)" + num + "之后" + name + "的余額為:" + m.get(name)); } public static int get(String name) { return m.get(name); } /** * 注意wait的用法,必須在loop中,,必須在擁有鎖的代碼塊中,。 前者是當(dāng)被notify的時(shí)候要重新進(jìn)行條件判斷,后者是為了釋放鎖,。 * * @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) + "不足,,開(kāi)始等待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接口的線程是怎么實(shí)例化的,。new Thread(new User()) * 這里成功展示了多個(gè)用戶存取同一個(gè)賬戶的多線程實(shí)例,,通過(guò)多線程同步,保證了安全的執(zhí)行,。 * @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(); } } } 6、下面是自己寫的,,一個(gè)經(jīng)典的消費(fèi)者和生產(chǎn)者的例子:
1,、ObjLock.java
/**
* 生產(chǎn)者(producer)與消費(fèi)者(consumer)問(wèn)題 * 說(shuō)明事物鎖與多線程同步 * 生產(chǎn)者生產(chǎn),消費(fèi)者消費(fèi),規(guī)則:但當(dāng)庫(kù)存為0時(shí),消費(fèi)者要消費(fèi)是不行的; * 但當(dāng)庫(kù)存為上限(這里是10)時(shí),生產(chǎn)者也不能生產(chǎn) */ public class ObjLock implements Runnable { private int count = 0; //產(chǎn)品數(shù)
private int locknum = 0; //上鎖的線程數(shù) public ObjLock(int n){ this.count = n; } //生產(chǎn) public synchronized void produce(){ while(count == 10){ locknum ++; try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } count ++; if(locknum > 0) locknum --; notify(); } //消費(fèi) public synchronized void consume(){ while(count == 0){ locknum ++; try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } count --; if(locknum > 0) locknum --; notify(); } public void run() { while(true){ if(Thread.currentThread().getName().substring(0, 8).equals("producer")) produce(); else if(Thread.currentThread().getName().substring(0, 8).equals("consumer")) consume(); System.out.println(Thread.currentThread().getName() + ": " + count + " ,locknum: " + locknum); } } }
2,、TestObjLock測(cè)試類
public static void main(String[] args) {
ObjLock ol = new ObjLock(1); Thread th1 = new Thread(ol,"producer"); Thread th2 = new Thread(ol,"consumer"); Thread th3 = new Thread(ol,"producer"); Thread th4 = new Thread(ol,"consumer"); Thread th5 = new Thread(ol,"producer"); Thread th6 = new Thread(ol,"consumer"); th1.start(); th2.start(); th3.start(); th4.start(); th5.start(); th6.start(); } 注意:這里所以生產(chǎn)和消費(fèi)都必須有notify(),,避免死鎖。不信的可以去掉其中一個(gè),,可以測(cè)試實(shí)現(xiàn),。
我記得大學(xué)學(xué)數(shù)據(jù)庫(kù)的時(shí)候,,也有說(shuō)到這類問(wèn)題。
|
|