java
並行:指兩個或多個事件在同一時刻編程
java.lang.Thread 類安全
public Thread() :分配一個新的線程對象。多線程
public Thread(String name) :分配一個指定名字的新的線程對象。併發
public Thread(Runnable target) :分配一個帶有指定目標新的線程對象。ide
public Thread(Runnable target,String name) :分配一個帶有指定目標新的線程對象並指定名字。測試
public void start() :致使此線程開始執行; Java虛擬機調用此線程的run方法。this
多線程的開啓方式只有調用start方法開啓。spa
public String getName() :獲取當前線程名稱。線程
public static Thread currentThread() :返回對當前正在執行的線程對象的引用。
1 /* 2 * 獲取線程的名稱: 3 * 1,使用Thread類中的getName()方法 4 * String getName() 返回該線程的名稱 5 * 2,能夠先獲取到當前正在執行的線程。使用線程中的方法getName()獲取線程的名稱 6 * static Thread currentThread() 返回對當前正在執行的線程對象的引用 7 * */ 8 9 10 public class MyThread extends Thread { 11 12 @Override 13 public void run(){ 14 // 獲取線程的名稱 15 16 // String name = getName(); 17 // System.out.println(name); 18 19 20 // Thread t = Thread.currentThread(); 21 // System.out.println(t); 22 // 23 // String name = t.getName(); 24 // System.out.println(name); 25 26 27 // 鏈式編程 28 System.out.println(Thread.currentThread().getName()); 29 } 30 } 31 32 33 ------------------------------------------------------------- 34 /* 35 * 線程的名稱: 36 * 主線程:main 37 * 新線程:Thread-0,Thread-1 38 * */ 39 40 public class Demo01GetThreadName { 41 public static void main(String[] args) { 42 // 建立Thread類的子類對象 43 MyThread mt = new MyThread(); 44 45 // 調用start方法,開啓新線程執行run方法 46 mt.start(); 47 48 new MyThread().start(); 49 new MyThread().start(); 50 51 System.out.println(Thread.currentThread().getName()); 52 53 } 54 }
public static void sleep(long millis) :使當前正在執行的線程以指定的毫秒數暫停(暫時中止執行)。
1 /* 2 * public static void sleep(long millis):使當前正在執行的線程以指定的毫秒數暫停(暫時中止執行)。 3 * 毫秒數結束以後,線程繼續執行 4 */ 5 6 public class Demo01Sleep { 7 public static void main(String[] args) { 8 // 模擬秒錶 9 for(int i =0;i<60;i++){ 10 11 System.out.println(i); 12 13 // 使用Thread類中的sleep方法讓程序睡眠1秒 14 try { 15 Thread.sleep(1000); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 } 20 } 21 }
java.long.Thread類:是描述線程的類,咱們想要實現多線程程序,就必須繼承Thread類
實現步驟:
1,建立一個Thread類的子類
2,在Thread類的子類中重寫Thread類中的run方法,設置線程任務(開啓線程要乾的事)
3,建立Thread類的子類對象
4,調用Thread類中的方法start方法,開啓新的線程,執行run方法【start方法自動去子類中找run方法執行】
void start() 使該線程開始執行;java虛擬機調用該線程的run方法。
結果是兩個線程併發的運行,當前線程(main線程)和另外一個線程(建立的新線程,執行其run方法)。
屢次啓動一個線程是非法的。特別是當前線程已經結束執行後,不能再從新啓動
1 //1.建立一個Thread類的子類 2 public class MyThread extends Thread { 3 4 //2,在Thread類的子類中重寫Thread類中的run方法,設置線程任務(開啓線程要乾的事) 5 @Override 6 public void run() { 7 for (int i = 0; i < 20; i++) { 8 System.out.println("run" + i); 9 } 10 } 11 } 12 13 14 --------------------------------------------------------------- 15 public class Demo01Thread { 16 17 public static void main(String[] args) { 18 // 3,建立Thread類的子類對象 19 MyThread my = new MyThread(); 20 21 //4,調用Thread類中的方法start方法,開啓新的線程,執行run方法 22 my.start(); 23 24 //my.start();錯誤的,屢次啓動一個線程非法 25 26 for (int i = 0; i < 20; i++) { 27 System.out.println("main" + i); 28 } 29 } 30 }
注意:java程序屬於搶佔式調度,那個線程優先級高,那個線程就優先執行。同一個優先級就隨機選一個執行
多線程的流程圖解和內存圖解:
java.long.Runnable:Runnable 接口應該由那些打算經過某一線程執行其實例的類來實現。類必須定義一個稱爲 run 的無參數方法。
java.long.Thread類的構造方法
Thread(Runnable target)分配新的 Thread 對象。
Thread(Runnable target, String name)分配新的 Thread 對象
實現步驟:
1,建立一個Runnable接口的實現類
2,在實現類中重寫Runnable接口中的run方法,設置線程任務。
3,建立一個Runnable接口的實現類對象
4,建立一個Thread類對象,參數中傳遞Runnable接口的實現類對象
5,調用Thread類中的start方法開啓新的線程執行run方法
1 // 1,建立一個Runnable接口的實現類 2 public class RunnableImp implements Runnable { 3 4 // 2,在實現類中重寫Runnable接口中的run方法,設置線程任務。 5 @Override 6 public void run() { 7 for (int i = 0; i < 20; i++) { 8 System.out.println(Thread.currentThread().getName() + i); 9 } 10 } 11 } 12 13 14 ----------------------------------------------------------------- 15 public class Dem01Runnable { 16 public static void main(String[] args) { 17 // 3,建立一個Runnable接口的實現類對象 18 RunnableImp run = new RunnableImp(); 19 20 // 4,建立一個Thread類對象,參數中傳遞Runnable接口的實現類對象 21 // Thread t = new Thread(run);// 打印線程名稱 22 Thread t = new Thread(new RunnableImp2());// 打印hello world 23 24 // 5,調用Thread類中的start方法開啓新的線程執行run方法 25 t.start(); 26 27 for (int i = 0; i < 20; i++) { 28 System.out.println(Thread.currentThread().getName()+i); 29 } 30 } 31 }
實現Runnable接口建立多線程的好處:
1,避免了單繼承的侷限性:一個類只能繼承一個類(一我的只能有一個親爹),類繼承了Thread類就不能繼承其餘的類。
實現了Runnable便可,還能夠繼承其餘的類,實現其餘的接口
2,加強了程序的擴展性,下降了程序的耦合性(解耦):實現Runnable接口的方式,把設置線程任務和開啓新線程進行了分離(解耦)
1)實現類中,重寫了run方法:用來設置線程任務。
2)建立Thread類對象,調用start方法:用來開啓多線程。
匿名內部類方法實現線程的建立
匿名:沒有名字
內部類:寫在其餘類內部的類
匿名內部類的做用:簡化代碼
把子類繼承父類,重寫父類的方法,建立子類對象集合一步完成
把實現類實現接口,重寫接口中的方法,建立實現類對象合成一步完成
匿名內部類的最終產物:子類/實現類對象,而這個類沒有名字
格式:
1 new 父類/接口(){ 2 重寫父類/接口中的方法 3 };
1 public class Demo01InnerClassThread { 2 3 public static void main(String[] args) { 4 // 線程的父類是Thread 5 // 原來的步驟:new MyThread子類,調用start方法 6 7 new Thread(){ 8 // 重寫run方法,設置線程任務 9 @Override 10 public void run(){ 11 for (int i = 0; i < 20; i++) { 12 System.out.println(Thread.currentThread().getName()+"哈哈哈"); 13 } 14 } 15 }.start(); 16 17 18 19 20 // 線程的接口Runnable 21 // Runnable r = new RunnableImp() 22 23 Runnable run = new Runnable(){ 24 @Override 25 public void run(){ 26 for (int i = 0; i < 20; i++) { 27 System.out.println(Thread.currentThread().getName()+"嗚嗚嗚"); 28 } 29 } 30 }; 31 32 new Thread(run).start(); 33 34 35 36 // 簡化接口的方式 37 new Thread(new Runnable() { 38 @Override 39 public void run() { 40 for (int i = 0; i < 20; i++) { 41 System.out.println(Thread.currentThread().getName()+"嗯嗯嗯"); 42 43 } 44 } 45 }).start(); 46 47 } 48 }
定義:若是有多個線程在同時運行,而這些線程可能會同時運行這段代碼。程序每次運行結果和單線程運行的結果是同樣 的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的。
下面使用一個賣票案例顯示可能出現的問題
1 /* 2 實現買票案例 3 */ 4 public class RunnableImp implements Runnable { 5 // 定義一個多個線程共享的票源 6 private int ticket = 100; 7 8 // 設置線程任務:買票 9 @Override 10 public void run() { 11 // 使用死循環,讓買票操做重複執行 12 while (true){ 13 // 判斷票是否存在 14 if (ticket>0){ 15 // 提升安全問題出現的機率,讓程序睡眠 16 try { 17 Thread.sleep(10); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 22 23 // 票存在,賣票 24 System.out.println(Thread.currentThread().getName()+"--->正在賣第"+ticket+"票"); 25 ticket--; 26 } 27 } 28 } 29 } 30 31 32 33 34 -------------------------------------------------------- 35 /* 36 模擬賣票案例: 37 建立三個線程,同時開啓,對共享的票出售 38 */ 39 public class Demo01Ticket { 40 public static void main(String[] args) { 41 // 建立Runnable接口的實現類對象 42 RunnableImp run = new RunnableImp(); 43 44 // 一個實現類讓三個線程訪問 45 46 // 建立Thread類對象,構造方法中傳遞Runnable接口的實現類對象 47 Thread t1 = new Thread(run); 48 Thread t2 = new Thread(run); 49 Thread t3 = new Thread(run); 50 51 // 開啓多線程 52 t1.start(); 53 t2.start(); 54 t3.start(); 55 56 // 輸出的有不存在的票和重複的票 57 } 58 }
輸出結果的部分截圖
發現程序出現了兩個問題:
1. 相同的票數,好比5這張票被賣了兩回。
2. 不存在的票,好比0票與-1票,是不存在的
線程不安全:這種問題,幾個窗口(線程)票數不一樣步了,這種問題稱爲線程不安全
當咱們使用多個線程訪問同一資源的時候,且多個線程中對資源有寫的操做,就容易出現線程安全問題。
要解決上述多線程併發訪問一個資源的安全性問題:也就是解決重複票與不存在票問題,Java中提供了同步機制 (synchronized)來解決
線程同步的三種實現方式:
1. 同步代碼塊。
2. 同步方法。
3. 鎖機制。
語法:
1 synchronized(鎖對象){ 2 可能會出現線程安全問題的代碼(訪問了共享數據的代碼) 3 }
注意:
1,經過代碼塊中的鎖對象,可使用任意的對象作爲鎖對象
2,可是必須保證多個線程使用的鎖對象是同一個
3,鎖對象的做用:把同步代碼塊鎖住,只讓一個線程在同步代碼塊中執行
1 public class RunnableImp implements Runnable { 2 // 定義一個多個線程共享的票源 3 private int ticket = 100; 4 5 // 建立一個鎖對象 6 Object obj = new Object(); 7 8 9 // 設置線程任務:買票 10 @Override 11 public void run() { 12 // 使用死循環,讓買票操做重複執行 13 while (true) { 14 // 建立同步代碼塊 15 synchronized (obj) { 16 // 判斷票是否存在 17 if (ticket > 0) { 18 // 提升安全問題出現的機率,讓程序睡眠 19 try { 20 Thread.sleep(100); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 25 26 // 票存在,賣票 27 System.out.println(Thread.currentThread().getName() + "--->正在賣第" + ticket + "票"); 28 ticket--; 29 } 30 } 31 } 32 } 33 } 34 35 36 37 38 ------------------------------------------------------------------------------- 39 public class Demo01Ticket { 40 public static void main(String[] args) { 41 // 建立Runnable接口的實現類對象 42 RunnableImp run = new RunnableImp(); 43 44 // 一個實現類讓三個線程訪問 45 46 // 建立Thread類對象,構造方法中傳遞Runnable接口的實現類對象 47 Thread t1 = new Thread(run); 48 Thread t2 = new Thread(run); 49 Thread t3 = new Thread(run); 50 51 // 開啓多線程 52 t1.start(); 53 t2.start(); 54 t3.start(); 55 } 56 }
格式:
1 修飾符 synchronized 返回值類型 方法名(參數列表){ 2 可能出現線程安全問題的代碼(訪問了共享數據的代碼) 3 }
使用步驟:
1,訪問了共享數據的代碼抽取出來,放在一個方法中
2,在方法上添加synchronized修飾符
1 public class RunnableImp implements Runnable { 2 // 定義一個多個線程共享的票源 3 private static int ticket = 100; 4 5 6 // 設置線程任務:買票 7 @Override 8 public void run() { 9 // 使用死循環,讓買票操做重複執行 10 while (true) { 11 // 判斷票是否存在 12 // payTicket(); 13 payTicketStatic(); 14 } 15 } 16 17 // 靜態同步方法 18 /* 19 * 鎖對象是誰? 20 * 不能是this 21 * this是建立對象以後產生的,靜態方法優先於對象 22 * 靜態方法的鎖對象是本類的class屬性--->class文件對象 23 * 24 * */ 25 26 public static synchronized void payTicketStatic() { 27 if (ticket > 0) { 28 // 提升安全問題出現的機率,讓程序睡眠 29 try { 30 Thread.sleep(10); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 35 36 // 票存在,賣票 37 System.out.println(Thread.currentThread().getName() + "--->正在賣第" + ticket + "票"); 38 ticket--; 39 } 40 } 41 42 43 // 定義一個同步方法 44 /* 45 * 同步方法也會把方法內部的代碼鎖住 46 * 只讓一個線程執行 47 * 同步方法的鎖對象是誰? 48 * 就是實現類對象 new RunnableImp() 49 * 也就是this 50 * 51 * */ 52 public synchronized void payTicket() { 53 if (ticket > 0) { 54 // 提升安全問題出現的機率,讓程序睡眠 55 try { 56 Thread.sleep(10); 57 } catch (InterruptedException e) { 58 e.printStackTrace(); 59 } 60 61 62 // 票存在,賣票 63 System.out.println(Thread.currentThread().getName() + "--->正在賣第" + ticket + "票"); 64 ticket--; 65 } 66 } 67 } 68 69 70 71 ------------------------------------------------------------------ 72 public class Demo01Ticket { 73 public static void main(String[] args) { 74 // 建立Runnable接口的實現類對象 75 RunnableImp run = new RunnableImp(); 76 77 // 一個實現類讓三個線程訪問 78 79 // 建立Thread類對象,構造方法中傳遞Runnable接口的實現類對象 80 Thread t1 = new Thread(run); 81 Thread t2 = new Thread(run); 82 Thread t3 = new Thread(run); 83 84 // 開啓多線程 85 t1.start(); 86 t2.start(); 87 t3.start(); 88 89 // 輸出的有不存在的票和重複的票 90 } 91 }
java.util.concurrent.locks.lock接口:Lock 實現提供了比使用 synchronized 方法和語句可得到的更普遍的鎖定操做。
Lock接口中的方法:
void lock() 獲取鎖。
void unlock() 釋放鎖。
Lock爲接口,因此咱們使用其實現類(ReentrantLock):java.util.concurrent.locks.ReentrantLock implements Lock
使用步驟:
1,在成員位置建立一個Lock實現類對象ReentrantLock對象
2,在可能出現安全問題的代碼前調用Lock接口中的方法lock獲取鎖
3,在可能出現安全問題的代碼後調用Lock接口中的方法unlock釋放鎖
1 import java.util.concurrent.locks.Lock; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 public class RunnableImp implements Runnable { 5 // 定義一個多個線程共享的票源 6 private int ticket = 100; 7 8 // 1,在成員位置建立一個Lock實現類對象ReentrantLock對象 9 Lock l = new ReentrantLock(); 10 11 12 13 // // 設置線程任務:買票 14 // @Override 15 // public void run() { 16 // // 使用死循環,讓買票操做重複執行 17 // while (true){ 18 // 19 // // 2,在可能出現安全問題的代碼前調用Lock接口中的方法lock獲取鎖 20 // l.lock(); 21 // // 判斷票是否存在 22 // if (ticket>0){ 23 // // 提升安全問題出現的機率,讓程序睡眠 24 // try { 25 // Thread.sleep(10); 26 // } catch (InterruptedException e) { 27 // e.printStackTrace(); 28 // } 29 // 30 // 31 // // 票存在,賣票 32 // System.out.println(Thread.currentThread().getName()+"--->正在賣第"+ticket+"票"); 33 // ticket--; 34 // } 35 // 36 // // 3,在可能出現安全問題的代碼後調用Lock接口中的方法unlock釋放鎖 37 // l.unlock(); 38 // } 39 // } 40 41 42 // 更好的寫法 43 44 // 設置線程任務:買票 45 @Override 46 public void run() { 47 // 使用死循環,讓買票操做重複執行 48 while (true){ 49 50 // 2,在可能出現安全問題的代碼前調用Lock接口中的方法lock獲取鎖 51 l.lock(); 52 // 判斷票是否存在 53 if (ticket>0){ 54 // 提升安全問題出現的機率,讓程序睡眠 55 try { 56 Thread.sleep(10); 57 // 票存在,賣票 58 System.out.println(Thread.currentThread().getName()+"--->正在賣第"+ticket+"票"); 59 ticket--; 60 } catch (InterruptedException e) { 61 e.printStackTrace(); 62 } finally { 63 // 不管程序出現不出現異常,都會把鎖釋放 64 // 3,在可能出現安全問題的代碼後調用Lock接口中的方法unlock釋放鎖 65 l.unlock(); 66 } 67 } 68 } 69 } 70 } 71 72 73 ----------------------------------------------------------- 74 public class Demo01Ticket { 75 public static void main(String[] args) { 76 // 建立Runnable接口的實現類對象 77 RunnableImp run = new RunnableImp(); 78 79 // 一個實現類讓三個線程訪問 80 81 // 建立Thread類對象,構造方法中傳遞Runnable接口的實現類對象 82 Thread t1 = new Thread(run); 83 Thread t2 = new Thread(run); 84 Thread t3 = new Thread(run); 85 86 // 開啓多線程 87 t1.start(); 88 t2.start(); 89 t3.start(); 90 } 91 }
各類狀態之間的互相轉換
多線程中在使用鎖對象的時候,大多數使用Object類來建立對象,由於該對象中有一些關於多線程的方法
Object類中的方法:
void wait(long timeout) :在其餘線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量前,致使當前線程等待。
void wait()在其餘線程調用此對象的 notify() 方法或 notifyAll() 方法前,致使當前線程等待。
void notify() 喚醒在此對象監視器上等待的單個線程。會執行wait以後的代碼
void notifyAll() :喚醒在此對象監視器上等待的全部線程。
1 /* 2 等待喚醒案例:線程之間的通訊 3 建立一個顧客線程(消費者):告知老闆要的包子的種類和數量,調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待) 4 建立一個老闆線程(消費者):花了5秒作包子,包子作好以後調用notify()方法,喚醒顧客吃包子 5 6 7 注意: 8 顧客和老闆線程必須使用同步代碼塊包裹起來,保證等待和喚醒只能有一個在執行 9 同步使用的鎖對象必須保證惟一 10 只有鎖對象才能調用wait和notify方法 11 */ 12 13 14 public class Demo01WaitAndNotify { 15 public static void main(String[] args) { 16 Object obj = new Object(); 17 18 // 建立一個老闆線程(生產者) 19 new Thread() { 20 @Override 21 public void run() { 22 // 一直作包子 23 while (true){ 24 // 花5秒作包子 25 try { 26 Thread.sleep(5000); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 // 保證等待和喚醒的線程只能有一個在執行 31 synchronized (obj) { 32 33 System.out.println("老闆5秒中以後作好包子,告知顧客能夠吃包子了"); 34 // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待) 35 obj.notify(); 36 } 37 } 38 } 39 }.start(); 40 41 42 new Thread(){ 43 @Override 44 public void run() { 45 // 一直等着買包子 46 while (true){ 47 // 保證等待和喚醒的線程只能有一個在執行 48 synchronized (obj) { 49 System.out.println("告知老闆要的包子種類和數量"); 50 // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待) 51 try { 52 obj.wait(); 53 } catch (InterruptedException e) { 54 e.printStackTrace(); 55 } 56 // 喚醒以後執行的代碼 57 System.out.println("包子作好了,開吃"); 58 System.out.println("---------------------"); 59 } 60 } 61 } 62 }.start(); 63 } 64 }
1 public class Demo02WaitAndNotify { 2 public static void main(String[] args) { 3 4 Object obj = new Object(); 5 6 7 new Thread(){ 8 @Override 9 public void run() { 10 // 一直等着買包子 11 while (true){ 12 // 保證等待和喚醒的線程只能有一個在執行 13 synchronized (obj) { 14 System.out.println("顧客1:告知老闆要的包子種類和數量"); 15 // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待) 16 try { 17 obj.wait(); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 // 喚醒以後執行的代碼 22 System.out.println("顧客1:包子作好了,開吃"); 23 System.out.println("---------------------"); 24 } 25 } 26 } 27 }.start(); 28 29 new Thread(){ 30 @Override 31 public void run() { 32 // 一直等着買包子 33 while (true){ 34 // 保證等待和喚醒的線程只能有一個在執行 35 synchronized (obj) { 36 System.out.println("顧客2:告知老闆要的包子種類和數量"); 37 // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待) 38 try { 39 obj.wait(); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 // 喚醒以後執行的代碼 44 System.out.println("顧客2:包子作好了,開吃"); 45 System.out.println("---------------------"); 46 } 47 } 48 } 49 }.start(); 50 51 52 // 建立一個老闆線程(生產者) 53 new Thread() { 54 @Override 55 public void run() { 56 // 一直作包子 57 while (true){ 58 // 花5秒作包子 59 try { 60 Thread.sleep(5000); 61 } catch (InterruptedException e) { 62 e.printStackTrace(); 63 } 64 // 保證等待和喚醒的線程只能有一個在執行 65 synchronized (obj) { 66 67 System.out.println("老闆5秒中以後作好包子,告知顧客能夠吃包子了"); 68 // 調用wait方法,放棄CPU的執行,進入到waiting狀態(無限等待) 69 // 若是有多個等待線程隨機喚醒一個等待 70 //obj.notify(); 71 72 //若是多個等待的線程喚醒全部 73 obj.notifyAll(); 74 } 75 } 76 } 77 }.start(); 78 } 79 }
1 /* 2 資源類:包子類 3 設置包子的屬性 4 皮 5 陷 6 包子的狀態: 有 true,沒有 false 7 */ 8 public class BaoZi { 9 //皮 10 String pi; 11 //陷 12 String xian; 13 //包子的狀態: 有 true,沒有 false,設置初始值爲false沒有包子 14 boolean flag = false; 15 16 } 17 18 19 ------------------------------------------------------------------ 20 /* 21 消費者(吃貨)類:是一個線程類,能夠繼承Thread 22 設置線程任務(run):吃包子 23 對包子的狀態進行判斷 24 false:沒有包子 25 吃貨調用wait方法進入等待狀態 26 true:有包子 27 吃貨吃包子 28 吃貨吃完包子 29 修改包子的狀態爲false沒有 30 吃貨喚醒包子鋪線程,生產包子 31 */ 32 public class ChiHuo extends Thread{ 33 //1.須要在成員位置建立一個包子變量 34 private BaoZi bz; 35 36 //2.使用帶參數構造方法,爲這個包子變量賦值 37 public ChiHuo(BaoZi bz) { 38 this.bz = bz; 39 } 40 //設置線程任務(run):吃包子 41 @Override 42 public void run() { 43 //使用死循環,讓吃貨一直吃包子 44 while (true){ 45 //必須同時同步技術保證兩個線程只能有一個在執行 46 synchronized (bz){ 47 //對包子的狀態進行判斷 48 if(bz.flag==false){ 49 //吃貨調用wait方法進入等待狀態 50 try { 51 bz.wait(); 52 } catch (InterruptedException e) { 53 e.printStackTrace(); 54 } 55 } 56 57 //被喚醒以後執行的代碼,吃包子 58 System.out.println("吃貨正在吃:"+bz.pi+bz.xian+"的包子"); 59 //吃貨吃完包子 60 //修改包子的狀態爲false沒有 61 bz.flag = false; 62 //吃貨喚醒包子鋪線程,生產包子 63 bz.notify(); 64 System.out.println("吃貨已經把:"+bz.pi+bz.xian+"的包子吃完了,包子鋪開始生產包子"); 65 System.out.println("----------------------------------------------------"); 66 } 67 } 68 } 69 } 70 71 72 ------------------------------------------------------------------- 73 /* 74 生產者(包子鋪)類:是一個線程類,能夠繼承Thread 75 設置線程任務(run):生產包子 76 對包子的狀態進行判斷 77 true:有包子 78 包子鋪調用wait方法進入等待狀態 79 false:沒有包子 80 包子鋪生產包子 81 增長一些趣味性:交替生產兩種包子 82 有兩種狀態(i%2==0) 83 包子鋪生產好了包子 84 修改包子的狀態爲true有 85 喚醒吃貨線程,讓吃貨線程吃包子 86 87 注意: 88 包子鋪線程和包子線程關係-->通訊(互斥) 89 必須同時同步技術保證兩個線程只能有一個在執行 90 鎖對象必須保證惟一,可使用包子對象做爲鎖對象 91 包子鋪類和吃貨的類就須要把包子對象做爲參數傳遞進來 92 1.須要在成員位置建立一個包子變量 93 2.使用帶參數構造方法,爲這個包子變量賦值 94 */ 95 public class BaoZiPu extends Thread{ 96 //1.須要在成員位置建立一個包子變量 97 private BaoZi bz; 98 99 //2.使用帶參數構造方法,爲這個包子變量賦值 100 public BaoZiPu(BaoZi bz) { 101 this.bz = bz; 102 } 103 104 //設置線程任務(run):生產包子 105 @Override 106 public void run() { 107 //定義一個變量 108 int count = 0; 109 //讓包子鋪一直生產包子 110 while(true){ 111 //必須同時同步技術保證兩個線程只能有一個在執行 112 synchronized (bz){ 113 //對包子的狀態進行判斷 114 if(bz.flag==true){ 115 //包子鋪調用wait方法進入等待狀態 116 try { 117 bz.wait(); 118 } catch (InterruptedException e) { 119 e.printStackTrace(); 120 } 121 } 122 123 //被喚醒以後執行,包子鋪生產包子 124 //增長一些趣味性:交替生產兩種包子 125 if(count%2==0){ 126 //生產 薄皮三鮮餡包子 127 bz.pi = "薄皮"; 128 bz.xian = "三鮮餡"; 129 }else{ 130 //生產 冰皮 牛肉大蔥陷 131 bz.pi = "冰皮"; 132 bz.xian = "牛肉大蔥陷"; 133 134 } 135 count++; 136 System.out.println("包子鋪正在生產:"+bz.pi+bz.xian+"包子"); 137 //生產包子須要3秒鐘 138 try { 139 Thread.sleep(3000); 140 } catch (InterruptedException e) { 141 e.printStackTrace(); 142 } 143 //包子鋪生產好了包子 144 //修改包子的狀態爲true有 145 bz.flag = true; 146 //喚醒吃貨線程,讓吃貨線程吃包子 147 bz.notify(); 148 System.out.println("包子鋪已經生產好了:"+bz.pi+bz.xian+"包子,吃貨能夠開始吃了"); 149 } 150 } 151 } 152 } 153 154 155 -------------------------------------------------------------- 156 /* 157 測試類: 158 包含main方法,程序執行的入口,啓動程序 159 建立包子對象; 160 建立包子鋪線程,開啓,生產包子; 161 建立吃貨線程,開啓,吃包子; 162 */ 163 public class Demo { 164 public static void main(String[] args) { 165 //建立包子對象; 166 BaoZi bz =new BaoZi(); 167 //建立包子鋪線程,開啓,生產包子; 168 new BaoZiPu(bz).start(); 169 //建立吃貨線程,開啓,吃包子; 170 new ChiHuo(bz).start(); 171 } 172 }
java.util.concurrent.Executors:線程池工程類,用來生產線程池
Executors類中的靜態方法:static ExecutorService newFixedThreadPool(int nThreads):建立一個可重用固定線程數的線程池
參數:int nThreads:建立線程池中包含的線程數量
返回值:ExecutorService接口:返回的是ExecutorService接口的實現類對象,可使用ExecutorService接收
java.util.ExecutorService:線程池接口。用來從線程池中獲取線程,調用start方法,執行線程任務
提交一個Runnable任務用於執行:submit(Runnable task)
關閉/銷燬 線程池的方法:void shutdown()
使用步驟:
1,使用線程池的工廠類Executors裏面提供的靜態方法newFixedThreadPool生產一個指定線程數量的線程池
2,建立一個類,實現Runnable接口,重寫run方法,設置線程任務
3,調用ExecutorService中的方法submit,傳遞線程任務(實現類),開啓線程執行run方法
4,調用ExecutorService中的方法shutdown銷燬線程池(不建議執行)
1 public class RunableImpl implements Runnable { 2 3 @Override 4 public void run(){ 5 System.out.println(Thread.currentThread().getName()+"建立了一個新的線程並執行"); 6 } 7 } 8 9 10 ------------------------------------------------------------------ 11 import java.util.concurrent.ExecutorService; 12 import java.util.concurrent.Executors; 13 14 public class Demo01ThreadPool { 15 16 public static void main(String[] args) { 17 ExecutorService es = Executors.newFixedThreadPool(2); 18 19 es.submit(new RunableImpl());//pool-1-thread-2建立了一個新的線程並執行 20 es.submit(new RunableImpl());//pool-1-thread-1建立了一個新的線程並執行 21 es.submit(new RunableImpl());//pool-1-thread-1建立了一個新的線程並執行 22 es.submit(new RunableImpl());//pool-1-thread-1建立了一個新的線程並執行 23 24 // 4,調用ExecutorService中的方法shutdown銷燬線程池(不建議執行) 25 es.shutdown(); 26 } 27 }
---------------------------