線程安全java
緣由:當多個線程訪問一個共享的資源時,會產生線程安全的問題。c++
解決方法:用synchronized關鍵字作爲多線程併發環境的執行有序性的保證手段之一。當一段代碼會修改共享變量,這一段代碼成爲互斥區或臨界區,爲了保證共享變量的正確性,synchronized標示了臨界區。典型的用法以下:安全
synchronized(鎖){ //這個鎖是共享對象多線程
臨界區代碼 併發
}ide
/** * 兩個線程共同打印a~z,考慮線程安全 * */ public class PrintLetters implements Runnable { private char c = 'a'; public synchronized boolean print() { if (c <= 'z') { System.out.println(Thread.currentThread().getName() + " --- " + c); try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } c++; return true; } return false; } @Override public void run() { boolean flag = print(); while (flag) { flag = print(); } } public static void main(String[] args) { PrintLetters printLetters = new PrintLetters(); Thread thread1 = new Thread(printLetters); Thread thread2 = new Thread(printLetters); thread1.setName("線程-1"); thread2.setName("線程-2"); thread1.start(); thread2.start(); } }
上面代碼屬於public synchronized void add(int num)狀況,其鎖就是這個方法所在的對象。同理,若是方法是public static synchronized void add(int num),那麼鎖就是這個方法所在的class。spa
線程間的相互做用:線程之間須要一些協調通訊,來共同完成一件任務。能夠調用java.lang.Object中的wait()、notify()和notifyAll()方法,這些方法是final的,不能被重寫。.net
wait():使當前線程進入放棄對象鎖進入wait pool中等待。直到其餘線程調用notify()或notifyAll()方法喚醒該線程。要確保線程調用wait()方法時擁有對象鎖,即wait()方法必須在synchronized方法或者synchronized方法塊中調用。
線程
notify()、notifyAll():code
notify():喚醒一個處於等待當前對象鎖的線程。若是多個線程處於等待狀態,會隨機選擇一個線程喚醒。同時,被喚醒的線程暫時不能被執行,須要等到當前線程放棄對象鎖。見:http://my.oschina.net/u/1757476/blog/420169 線程的生命週期。
notifyAll():喚醒全部等待當前對象鎖的線程,變成等待該對象上的鎖。一旦該對象被解鎖,它們就會去競爭。
/** * 模擬賣票:劉、關、張三人買票,售票員只有一張5元零錢,票價5元;張飛拿20元排在劉、關前面,劉、關二人各拿了5元。 * 線程間的通訊 */ public class TicketHouse implements Runnable { private int fiveCount = 1, tenCount = 0, twentyCount = 0; public synchronized void buy() { String name = Thread.currentThread().getName(); //張飛 20元 if ("張飛".equals(name)) { if (fiveCount < 3) { try { System.out.println("5元面值:" + fiveCount + ". 張飛等待..."); wait(); System.out.println("賣一張票給" + name + ",找零15. 5元面值:" + fiveCount); } catch (InterruptedException e) { e.printStackTrace(); } } } else if ("關羽".equals(name) || "劉備".equals(name)) { fiveCount++; System.out.println("賣一張票給" + name + ",錢正好. 5元面值:" + fiveCount); } if (fiveCount == 3) { notifyAll(); } } @Override public void run() { buy(); } public static void main(String[] args) { Runnable runnable = new TicketHouse(); Thread th1 = new Thread(runnable); Thread th2 = new Thread(runnable); Thread th3 = new Thread(runnable); th1.setName("劉備"); th2.setName("關羽"); th3.setName("張飛"); th3.start(); th1.start(); th2.start(); } }
/** * 兩個線程交替打印a~z * */ public class PrintLetters2 implements Runnable { private char c = 'a'; @Override public void run() { while (c <= 'z') { print(); } } public synchronized void print() { if (c <= 'z') { System.out.println(Thread.currentThread().getName() + "---" + c); c++; notify(); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Runnable runnable = new PrintLetters2(); Thread th1 = new Thread(runnable); Thread th2 = new Thread(runnable); th1.setName("線程-1"); th2.setName("線程-2"); th1.start(); th2.start(); } }