-------------------------學前必讀----------------------------------java
學習不能快速成功,但必定能夠快速入門
總體課程思路:
1.實踐爲主,理論化偏少
2.課程筆記有完整的案例和代碼,(爲了學習效率)再開始以前我會簡單粗暴的介紹知識點案例思路,
有基礎的同窗聽了以後能夠直接結合筆記寫代碼,
若是沒聽懂再向下看視頻,我會手把手編寫代碼和演示測試結果;
3.重要提示,學編程和學游泳同樣,多實踐學習效率才高,理解才透徹;
4.編碼功底差的建議每一個案例代碼寫三遍,至於爲何...<<賣油翁>>...老祖宗的智慧編程
-------------------------------------------------------------------------多線程
1.synchronized鎖住的是括號裏的對象,而不是代碼。
2.經常使用鎖對象(this,字節碼)
1. 當synchronized (TicketThread.class),程序執行正常,只用一份字節碼,誰擁有字節碼誰就有執行權
2. synchronized (lockObject),程序執行正常,由於整個應用上下文只有一個lockObject對象,誰擁有lockObject對象,誰就有執行權
3. synchronized (this),有重複售票的現象,this表明當前對象,擁有當前對象就擁有執行權,而在時間調用中咱們建立了多個TicketThread對象,所以鎖是無效的;
4. synchronized (num),有重複售票現象,由於num是變更的,若是有2個線程都是num=99那麼只能有其中一個線程有執行權,
可是若是num數不同能夠同時執行,這裏爲後面講出售不一樣的班次的車票時提升鎖的效率埋下伏筆ide
3.使用同步方法是,不須要手動添加同步監聽對象,
若是是實例方法,那麼默認的同步監聽對象就是this,
若是是靜態方法,默認的同步監聽對象是類的字節碼對象;
4.注意,當使用字節碼鎖時,使用了synchronized (TicketThread.class) 的地方都會被鎖住,即便在不一樣的方法內;性能
5.ReentrantLock提供了比使用 synchronized 方法和語句可得到的更普遍的鎖定操做,性能更高。學習
6.若是要對不一樣的數據加鎖,應該怎麼辦,好比購票業務中只對同一班次的車票加鎖.測試
需求:編寫模擬售票程序,驗證上面的結論ui
測試對象this
1 package com.wfd360.thread; 2 3 import com.wfd360.thread.demo05Syn.TicketThread; 4 import org.junit.Test; 5 6 /** 7 * @author 姿式帝-博客園 8 * @address https://www.cnblogs.com/newAndHui/ 9 * @WeChat 851298348 10 * @create 05/05 10:53 11 * @description 線程同步 12 * 1.同步代碼塊 13 * synchronized鎖住的是括號裏的對象,而不是代碼。 14 * 2.同步方法 15 * 使用同步方法是,不須要手動添加同步監聽對象, 16 * 若是是實例方法,那麼默認的同步監聽對象就是this, 17 * 若是是靜態方法,默認的同步監聽對象是類的字節碼對象; 18 * 3.同步鎖-ReentrantLock 19 */ 20 public class Test08Syn { 21 /** 22 * 測試: 23 */ 24 @Test 25 public void testTicketThread() throws InterruptedException { 26 System.out.println("---test start-------"); 27 TicketThread thread1 = new TicketThread(); 28 thread1.setName("窗口1"); 29 TicketThread thread2 = new TicketThread(); 30 thread2.setName("窗口2"); 31 TicketThread thread3 = new TicketThread(); 32 thread3.setName("窗口3"); 33 // 開啓線程 34 thread1.start(); 35 thread2.start(); 36 thread3.start(); 37 Thread.sleep(10); 38 //測試普通方法可否被調用 39 //thread1.method1(); 40 System.out.println("==========等待售票============"); 41 Thread.sleep(10 * 1000); 42 System.out.println("---test end-------"); 43 } 44 }
線程對象編碼
1 package com.wfd360.thread.demo05Syn; 2 3 import java.util.concurrent.locks.ReentrantLock; 4 5 /** 6 * @author 姿式帝-博客園 7 * @address https://www.cnblogs.com/newAndHui/ 8 * @WeChat 851298348 9 * @create 05/04 11:55 10 * @description <p> 11 * 模擬多線程售票 12 * </p> 13 */ 14 public class TicketThread extends Thread { 15 // 假定票老是100張 16 private static Integer num = 100; 17 // 鎖對象 18 private static Object lockObject = new Object(); 19 // 同步鎖 20 private static final ReentrantLock lock = new ReentrantLock(); 21 22 @Override 23 public void run() { 24 while (num > 0) { 25 sellTicketLock(); 26 } 27 System.out.println("===售票結束==="); 28 } 29 30 /** 31 * 當使用字節碼鎖時,使用了synchronized (TicketThread.class) 的地方都會被鎖住,即便在不一樣的方法內 32 */ 33 public void method1() { 34 synchronized (TicketThread.class) { 35 System.out.println("=======method1============="); 36 } 37 } 38 39 /** 40 * 同步代碼塊實現 41 * 探討synchronized鎖的是什麼? 42 * synchronized鎖住的是括號裏的對象,而不是代碼。 43 * <p> 44 * 1. 當synchronized (TicketThread.class),程序執行正常,只用一份字節碼,誰擁有字節碼誰就有執行權 45 * 2. synchronized (lockObject),程序執行正常,由於整個應用上下文只有一個lockObject對象,誰擁有lockObject對象,誰就有執行權 46 * 3. synchronized (this),有重複售票的現象,this表明當前對象,擁有當前對象就擁有執行權,而在時間調用中咱們建立了多個TicketThread對象,所以鎖是無效的; 47 * 4. synchronized (num),有重複售票現象,由於num是變更的,若是有2個線程都是num=99那麼只能有其中一個線程有執行權, 48 * 可是若是num數不同能夠同時執行,這裏爲後面講出售不一樣的班次的車票時提升鎖的效率埋下伏筆 49 */ 50 private void sellTicket() { 51 synchronized (TicketThread.class) { 52 if (num > 0) { 53 // 獲取線程名稱 54 String threadName = this.getName(); 55 System.out.println(threadName + "-正在出售第" + num + "張票"); 56 // 模擬售票耗時20毫秒 57 try { 58 Thread.sleep(50); 59 } catch (InterruptedException e) { 60 e.printStackTrace(); 61 } 62 --num; 63 } 64 } 65 } 66 67 /** 68 * 同步方法實現鎖 69 * 使用同步方法是,不須要手動添加同步監聽對象, 70 * 若是是實例方法,那麼默認的同步監聽對象就是this, 71 * 若是是靜態方法,默認的同步監聽對象是類的字節碼對象; 72 * 1.實例方法 private synchronized void sellTicketSyn(),默認的同步監聽對象就是this,只能鎖住同一個對象,在當前使用會出現重複售票; 73 * 2.靜態方法 private static synchronized void sellTicketSyn(),同步監聽對象是類的字節碼對象,至關於全局鎖; 74 */ 75 private static void sellTicketSyn() { 76 if (num > 0) { 77 // 獲取線程名稱 78 // String threadName = this.getName(); 79 String threadName = Thread.currentThread().getName(); 80 System.out.println(threadName + "-正在出售第" + num + "張票"); 81 // 模擬售票耗時20毫秒 82 try { 83 Thread.sleep(50); 84 } catch (InterruptedException e) { 85 e.printStackTrace(); 86 } 87 --num; 88 } 89 } 90 91 /** 92 * 同步鎖-ReentrantLock 93 * 1.加鎖 lock.lock(); 94 * 2.必須手動釋放鎖 lock.unlock(); 95 */ 96 private void sellTicketLock() { 97 // 對代碼加鎖 98 lock.lock(); 99 try { 100 if (num > 0) { 101 // 獲取線程名稱 102 // String threadName = this.getName(); 103 String threadName = Thread.currentThread().getName(); 104 System.out.println(threadName + "-同步鎖正在出售第" + num + "張票"); 105 // 模擬售票耗時20毫秒 106 try { 107 Thread.sleep(50); 108 } catch (InterruptedException e) { 109 e.printStackTrace(); 110 } 111 --num; 112 } 113 } finally { 114 // 釋放鎖 115 lock.unlock(); 116 } 117 } 118 }
需求:模擬蓄水池流量限制,爲了簡單更明白的理解線程等待與通知,這裏假定模型爲極端狀況,
假設蓄水池的正常水位是1000噸,當有水時每次取走水1000噸,當沒有水時每次加入1000噸,讓這兩個動做交互進行
分析:
1.編寫水池對象,並提供當前水位字段,取水方法,加水方法
/**
* 加水方法
* 1.synchronized 對同一個水池採用同步斷定
* 2.判斷是否有水,有水則等待(this.wait()),不然加水
* 3.加完水後進行通知(this.notify())
*/
/**
* 取水方法
* 1.synchronized 對同一個水池採用同步斷定
* 2.判斷是否有水,無水則等待(this.wait()),不然取水
* 3.取完水後進行通知(this.notify())
*/
2.編寫2個線程,一個線程取水,一個線程加水
3.啓動測試,建立一個水池對象,將對象分別傳入取水線程和加水線程,並啓動線程
實現代碼
測試對象
1 package com.wfd360.thread; 2 3 import com.wfd360.thread.demo06Wait.AddPollThread; 4 import com.wfd360.thread.demo06Wait.Pool; 5 import com.wfd360.thread.demo06Wait.ReducePollThread; 6 7 /** 8 * @author 姿式帝-博客園 9 * @address https://www.cnblogs.com/newAndHui/ 10 * @WeChat 851298348 11 * @create 05/05 3:18 12 * @description <p> 13 * 需求:模擬蓄水池流量限制,爲了簡單更明白的理解線程等待與通知,這裏假定模型爲極端狀況, 14 * 假設蓄水池的正常水位是1000噸,當有水時每次取走水1000噸,當沒有水時每次加入1000噸,讓這兩個動做交互進行 15 * 分析: 16 * 1.編寫水池對象,並提供當前水位字段,取水方法,加水方法 17 * 2.編寫2個線程,一個線程取水,一個線程加水 18 * 3.啓動測試,建立一個水池對象,將對象分別傳入取水線程和加水線程 19 * 20 * </p> 21 */ 22 public class Test09Wait { 23 /** 24 * 測試加水取水交替進行,理解等待與通知 25 * 26 * @param args 27 */ 28 public static void main(String[] args) { 29 // 建立水池對象 30 Pool pool = new Pool(); 31 pool.setNum(1000); 32 // 建立加水線程,並啓動 33 new AddPollThread(pool, "加水線程").start(); 34 // 建立取水線程,並啓動 35 new ReducePollThread(pool, "取水線程").start(); 36 } 37 }
水池對象
1 package com.wfd360.thread.demo06Wait; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/05 3:28 8 * @description <p> 9 * 水池對象 10 * </p> 11 */ 12 public class Pool { 13 // 水池編號 14 private Integer id; 15 // 水池當前水量 16 private Integer num; 17 18 /** 19 * 加水方法 20 * 1.synchronized 對同一個水池採用同步斷定 21 * 2.判斷是否有水,有水則等待,不然加水 22 * 3.加完水後進行通知 23 */ 24 public synchronized void addPoll() throws Exception { 25 String name = Thread.currentThread().getName(); 26 // 當水位大於或等於1000時,線程進入等待狀態 27 if (num >= 1000) { 28 System.out.println(name + "-進入等待加水狀態,num=" + num); 29 this.wait(); 30 } 31 System.out.println(name + "-準備加水,num=" + num); 32 this.num += 1000; 33 System.out.println(name + "-加水完成,num=" + num); 34 this.notify(); 35 } 36 /** 37 * 取水方法 38 * 1.synchronized 對同一個水池採用同步斷定 39 * 2.判斷是否有水,無水則等待,不然取水 40 * 3.取完水後進行通知 41 */ 42 public synchronized void reducePoll() throws Exception { 43 String name = Thread.currentThread().getName(); 44 // 當水位大於或等於1000時,線程進入等待狀態 45 if (num < 1000) { 46 System.out.println(name + "-進入等待--取水--狀態,num=" + num); 47 this.wait(); 48 } 49 System.out.println(name + "-準備--取水--,num=" + num); 50 this.num -= 1000; 51 System.out.println(name + "---取水--完成,num=" + num); 52 this.notify(); 53 } 54 55 public Integer getId() { 56 return id; 57 } 58 59 public void setId(Integer id) { 60 this.id = id; 61 } 62 63 public Integer getNum() { 64 return num; 65 } 66 67 public void setNum(Integer num) { 68 this.num = num; 69 } 70 }
取水線程
1 package com.wfd360.thread.demo06Wait; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/05 3:41 8 * @description 9 */ 10 public class ReducePollThread extends Thread { 11 private Pool pool; 12 13 // 建立線程必須傳入水池對象 14 public ReducePollThread(Pool pool,String name) { 15 this.pool = pool; 16 this.setName(name); 17 } 18 19 @Override 20 public void run() { 21 // 循環取水100次 22 for (int i = 0; i < 100; i++) { 23 try { 24 pool.reducePoll(); 25 } catch (Exception e) { 26 e.printStackTrace(); 27 } 28 } 29 } 30 }
加水線程
1 package com.wfd360.thread.demo06Wait; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/05 3:41 8 * @description 9 */ 10 public class AddPollThread extends Thread { 11 private Pool pool; 12 13 // 建立線程必須傳入水池對象 14 public AddPollThread(Pool pool,String name) { 15 this.pool = pool; 16 // 設置線程名稱 17 this.setName(name); 18 } 19 20 @Override 21 public void run() { 22 // 循環加水100次 23 for (int i = 0; i < 100; i++) { 24 try { 25 pool.addPoll(); 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 } 30 } 31 }
測試結果
完美!
系統化的在線學習:點擊進入學習