JAVA多线程同步
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) { //code.... }。表示一個線程要執行該代碼塊,必須獲得obj的鎖。這樣做的目的是減小鎖的粒度,保證當不同塊所需的鎖不沖突時不用對整個對象加鎖。利用零長度的byte數組對象做obj非常經濟。
4 atomic action(原子操作):
??????? 在JAVA中,以下兩點操作是原子操作。但是c和c++中并不如此。
??????? 第一點:對引用變量和除了long和double之外的原始數據類型變量進行讀寫。
??????? 第二點:對所有聲明為volatile的變量(包括long和double)的讀寫。
??????? 另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依賴于同步機制的線程安全的類和方法。
5 一個例子,該例子模仿多人存取同一個賬戶:
Account類:
package com.synchronize;
import java.util.HashMap;
import 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
??????? 該方法屬于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) { //code.... }。表示一個線程要執行該代碼塊,必須獲得obj的鎖。這樣做的目的是減小鎖的粒度,保證當不同塊所需的鎖不沖突時不用對整個對象加鎖。利用零長度的byte數組對象做obj非常經濟。
4 atomic action(原子操作):
??????? 在JAVA中,以下兩點操作是原子操作。但是c和c++中并不如此。
??????? 第一點:對引用變量和除了long和double之外的原始數據類型變量進行讀寫。
??????? 第二點:對所有聲明為volatile的變量(包括long和double)的讀寫。
??????? 另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依賴于同步機制的線程安全的類和方法。
5 一個例子,該例子模仿多人存取同一個賬戶:
Account類:
package com.synchronize;
import java.util.HashMap;
import 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
轉載于:https://blog.51cto.com/2960629/727904
總結
- 上一篇: 让人期待的2011年度最佳 jQuery
- 下一篇: 监控系统安装配置文档(Nagios+Ca