1. 線程的概念編程
1.1多進程與多線程安全
進程:一個正在執行的程序.每一個進程執行都有一個執行順序,該順序是一個執行路徑,或叫一個控制單元. 一個進程至少有一個線程.多線程
線程:就是進程中的一個獨立的控制單元. 線程控制這進程的執行.ide
多進程的缺點:進程切換開銷大;進程間的通訊很不方便。函數
多線程: 指的是在單個程序中能夠同時運行多個不一樣的線程,執行不一樣的任務,線程切換的開銷小 。this
1.2線程的狀態spa
Java語言使用Thread類及其子類的對象來表示線程,新建的線程在它的一個完整的生命週期一般要經歷5種狀態.線程
凍結狀態:在sleep和wait時, 既沒有運行資格,有沒有執行權3d
阻塞狀態: 具有運行資格, 沒有執行權code
1.3線程調度與優先級(多線程的特性:隨機性)
Java採用搶佔式調度策略,下面幾種狀況下,當前線程會放棄CPU:
(1)當前時間片用完;
(2)線程在執行時調用了yield() 或sleep() 方法主動放棄;
(3)進行I/O 訪問,等待用戶輸入,致使線程阻塞;或者爲等候一個條件變量,線程調用wait()方法;
(4)有高優先級的線程參與調度。
線程的優先級用數字來表示,範圍從1~10。主線程的默認優先級爲5
Thread.MIN_PRIORITY=1
Thread.NORM_PRIORITY=5
Thread.MAX_PRIORITY=10
2. 多線程編程方法
2.1 Thread類簡介
Thread類綜合了Java程序中一個線程須要擁有的屬性和方法,其構造方法以下:
public Thread (ThreadGroup group,Runnable target,String name);
public Thread();
public Thread(Runnable target);
public Thread(Runnable target,String name);
public Thread(String name);
public Thread(ThreadGroup group,Runnable target);
public Thread(ThreadGroup group,String name);
Thread類的主要方法以及功能如表
2.2 繼承Thread類實現多線程
須要重寫run方法實現線程的任務.須要注意的是,程序中不要直接調用此方法,而是調用線程對象的start()方法啓動線程,讓其進入可調度狀態,線程得到調度自動執行run()方法.
2.3 實現Runnable接口編寫多線程
經過 Thread 類的構造函數public Thread(Runnable target)能夠將一個Runnable 接口對象傳遞給線程,線程在調度時將自動調用Runnable 接口對象的run方法。
實現方式和繼承方法的區別:
實現方式的好處:避免了單繼承的侷限性.在定義線程時,建議使用實現方式.
3. 線程資源的同步處理
3.1 臨界資源問題
Java對於多線程的安全問題提供了專業的解決方式: 就是同步代碼塊
synchronized(對象) {
須要被同步的代碼
}
對象如同鎖,持有鎖的線程能夠在同步中執行.
沒有持有鎖的線程及時獲取cpu的執行權,也進不去,由於沒有獲取鎖.
同步的前提:
1.必需要有兩個或者以上的線程
2.必需要多個線程使用同一個鎖
好處:解決了多線程的安全問題
弊端:多個線程須要判斷鎖,較爲消耗資源.
同步函數使用的是哪一個鎖:
函數須要被對象調用,那麼函數都有一個所屬對象的引用.就是this.
因此同步函數使用的鎖是this鎖.靜態函數使用的是該方法所在類的字節碼文件對象.
懶漢式加強
1 class Single{ 2 private Single(){} 3 private static Single s = null; 4 public static Single getInstance(){ 5 if(s == null){ 6 synchronized(Single.class){ 7 if(s == null) 8 s = new Single(); 9 } 10 } 11 return s; 12 } 13 }
3.2 wait()和notify()
3.3 避免死鎖
多個線程相互等待對方釋放持有的鎖,而且在獲得對方鎖以前不釋放本身的鎖.(本身能寫一個死鎖代碼)
1 /* 2 * 死鎖: 同步中嵌套同步容易發生 3 */ 4 5 public class LockDemo { 6 7 public static void main(String[] args) { 8 Test t = new Test(false); 9 Test t2 = new Test(true); 10 new Thread(t).start(); 11 new Thread(t2).start(); 12 13 } 14 15 } 16 17 class Test implements Runnable{ 18 private boolean flag; 19 Test(boolean flag){ 20 this.flag = flag; 21 } 22 public void run(){ 23 if(flag){ 24 try{Thread.sleep(30);}catch(Exception e){} 25 synchronized(KK.oa){ 26 System.out.println("if a"); 27 synchronized(KK.ob){ 28 System.out.println("if b"); 29 } 30 } 31 } 32 else{ 33 synchronized(KK.ob){ 34 System.out.println("else b"); 35 synchronized(KK.oa){ 36 System.out.println("else a"); 37 } 38 } 39 } 40 } 41 } 42 43 //自定義兩個鎖 44 class KK{ 45 static Object oa = new Object(); 46 static Object ob = new Object(); 47 }
4. 線程間通訊
思考1: wait(),notify(),nofifyAll()用來操做線程爲何定義在Object類中?
1.這些方法存在同步中,用來操做同步中的線程,必需要標誌它們所操做線程的只有鎖.
2.使用這些方法必須標識出所屬同步的鎖,只有同一個鎖上被等待線程,能夠被同一個鎖上notify喚醒.不能夠對不一樣鎖中的線程進行喚醒.
3.鎖能夠是任意對象,因此任意對象調用的方法必定定義Object類中.
思考2:wait(),sleep()有什麼區別?
wait():釋放資源,釋放鎖
sleep():釋放資源,不釋放鎖
1 //線程通訊: 取mike, 取麗麗 2 public class Demo2 { 3 4 public static void main(String[] args) { 5 Person p = new Person(); 6 new Thread(new Input(p)).start(); 7 new Thread(new Output(p)).start(); 8 } 9 10 } 11 12 class Person{ 13 private String name ; 14 private String sex ; 15 private boolean flag = false; 16 17 public synchronized void set(String name, String sex){ 18 //flag爲true,表示已存在 19 if(flag){ 20 try {wait();}catch (InterruptedException e) {e.printStackTrace();} 21 } 22 //flag爲false,表示沒人可加 23 this.name = name; 24 this.sex = sex; 25 flag = true; 26 this.notify(); 27 } 28 29 public synchronized void out(){ 30 //flag爲false,表示無人無法取 31 if(!flag){ 32 try {wait();}catch (InterruptedException e) {e.printStackTrace();} 33 } 34 //flag爲true,表示有人能夠取出 35 System.out.println(toString()); 36 flag = false; 37 this.notify(); 38 } 39 40 public String toString(){ 41 return "姓名:"+name+"性別:"+sex; 42 } 43 } 44 45 class Input implements Runnable{ 46 private Person p; 47 48 Input(Person p){ 49 this.p = p; 50 } 51 52 public void run(){ 53 boolean flag = false; 54 while(true){ 55 if(flag){ 56 p.set("likai","男"); 57 flag = false; 58 } 59 else{ 60 p.set("tangll","女"); 61 flag = true; 62 } 63 } 64 } 65 } 66 67 class Output implements Runnable{ 68 private Person p; 69 70 Output(Person p){ 71 this.p = p; 72 } 73 74 public void run(){ 75 while(true){ 76 p.out(); 77 } 78 } 79 }
1 public class Demo3 { 2 3 public static void main(String[] args) { 4 Bridge b = new Bridge(); 5 for(int i=1;i<6;i++){ 6 new Thread(new Person(b,"由北向南第"+i+"人")).start(); 7 } 8 9 for(int i=1;i<7;i++){ 10 new Thread(new Person(b,"由南向北第"+i+"人")).start(); 11 } 12 } 13 } 14 15 class Bridge{ 16 private String name; 17 private boolean flag; 18 public synchronized void UpBridge(){ 19 if(flag){ 20 try { 21 wait(); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 flag = true; 27 28 } 29 public synchronized void DownBridge(){ 30 flag = false; 31 notifyAll(); 32 } 33 } 34 35 class Person implements Runnable{ 36 private Bridge bridge; 37 private String pName; 38 39 Person(Bridge bridge, String pName){ 40 this.bridge = bridge; 41 this.pName = pName; 42 } 43 44 public void run() { 45 bridge.UpBridge(); 46 try{ 47 Thread.sleep(80); 48 }catch(InterruptedException e){} 49 bridge.DownBridge(); 50 System.out.println(pName); 51 } 52 53 }
生產消費者問題
生產消費者問題--JDK1.5提供的多線程升級解決方法
將同步synchronized替換成Lock操做.
將Object中的wait,notify,notifyAll方法替換成Condition對象.
該對象能夠Lock鎖進行獲取.
該實例中,實現了本方只喚醒對方的操做.
中止線程
1.定義循環結束標記
由於線程運行代碼通常都是循環,只是控制了循環便可.
2.使用interrupt(中斷)方法: 該方法是強制結束線程的凍結狀態,使線程回到運行狀態中來.這樣就能夠操做標記結束.
(注)stop方法和suspend方法已過期不在再使用.
setDeamon()方法
標記爲後臺線程, 當全部前臺前程結束後會Java虛擬機退出. 該方法必須在啓動線程前調用.
join()方法
等待該線程終止, 實際上是搶奪CPU執行權.
當A線程執行到B線程的.join()方法, A就會等待. 等B線程都執行完, A纔會執行. join能夠用來臨時加入線程執行.
toString()
返回該線程的字符串表示形式,包括線程名稱,優先級和線程組
yield()
暫停當前執行的線程對象,並執行其餘線程