-------------------------學前必讀----------------------------------html
學習不能快速成功,但必定能夠快速入門
總體課程思路:
1.實踐爲主,理論化偏少
2.課程筆記有完整的案例和代碼,(爲了學習效率)再開始以前我會簡單粗暴的介紹知識點案例思路,
有基礎的同窗聽了以後能夠直接結合筆記寫代碼,
若是沒聽懂再向下看視頻,我會手把手編寫代碼和演示測試結果;
3.重要提示,學編程和學游泳同樣,多實踐學習效率才高,理解才透徹;
4.編碼功底差的建議每一個案例代碼寫三遍,至於爲何...<<賣油翁>>...老祖宗的智慧編程
-------------------------------------------------------------------------數組
線程(英語:thread)是操做系統可以進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運做單位。一條線程指的是進程中一個單一順序的控制流,一個進程中能夠併發多個線程,每條線程並行執行不一樣的任務。在Unix System V及SunOS中也被稱爲輕量進程(lightweight processes),但輕量進程更多指內核線程(kernel thread),而把用戶線程(user thread)稱爲線程。(來自百度百科)多線程
一個進程能夠有不少線程,每條線程並行執行不一樣的任務。併發
需求:模擬在計算上一邊聽歌一邊打遊戲ide
三種實現方案以下:學習
1 package com.wfd360.thread; 2 3 import com.wfd360.thread.demo01.GameRunnable; 4 import com.wfd360.thread.demo01.MusicRunnable; 5 import com.wfd360.thread.demo02.GameThread; 6 import com.wfd360.thread.demo02.MusicThread; 7 import org.junit.Test; 8 9 /** 10 * @author 姿式帝-博客園 11 * @address https://www.cnblogs.com/newAndHui/ 12 * @WeChat 851298348 13 * @create 05/03 5:27 14 * @description 需求分析: 15 * 1.模擬一邊打遊戲一邊聽音樂,在控制檯打印輸出模擬 16 * 2.把兩個業務封裝成獨立的線程,實現接口Runnable或繼承Thread,經過看源碼你會發現Thread類實現了接口Runnable,使用本質上這兩種方法時同樣的。 17 * 3.Thread類提供兩個方法,線程主題方法run,啓動線程方法start 18 */ 19 public class TestDemo { 20 /** 21 * 方式1:實現接口Runnable 22 */ 23 @Test 24 public void testRunnable() throws InterruptedException { 25 System.out.println("-------test start-------"); 26 // 實例對象 27 MusicRunnable music = new MusicRunnable(); 28 GameRunnable game = new GameRunnable(); 29 // 建立線程 30 Thread musicThread = new Thread(music); 31 Thread gameThread = new Thread(game); 32 // 啓動線程 33 musicThread.start(); 34 gameThread.start(); 35 System.out.println("--------等待其餘線程執行--------------"); 36 Thread.sleep(5 * 1000); 37 System.out.println("-------test end-------"); 38 } 39 40 /** 41 * 方式2:繼承Thread 42 */ 43 @Test 44 public void testThread() throws InterruptedException { 45 System.out.println("-------test start-------"); 46 // 建立線程 47 MusicThread musicThread = new MusicThread(); 48 GameThread gameThread = new GameThread(); 49 // 啓動線程 50 musicThread.start(); 51 gameThread.start(); 52 System.out.println("--------等待其餘線程執行--------------"); 53 Thread.sleep(5 * 1000); 54 System.out.println("-------test end-------"); 55 } 56 57 /** 58 * 方式3:簡寫,這種寫法通常咱們在作模擬測試的使用,在正式代碼中建議不使用,可讀性較差 59 */ 60 @Test 61 public void testThreadSimple() throws InterruptedException { 62 System.out.println("-------test start-------"); 63 // 建立線程 64 Thread musicThread = new Thread(() -> { 65 for (int i = 0; i < 100; i++) { 66 System.out.println("=======聽音樂中============" + i); 67 } 68 }); 69 Thread gameThread = new Thread(() -> { 70 for (int i = 0; i < 100; i++) { 71 System.out.println("=======打遊戲中============" + i); 72 } 73 }); 74 // 啓動線程 75 musicThread.start(); 76 gameThread.start(); 77 System.out.println("--------等待其餘線程執行--------------"); 78 Thread.sleep(5 * 1000); 79 System.out.println("-------test end-------"); 80 } 81 }
實現接口Runnable測試
1 package com.wfd360.thread.demo01; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/03 5:31 8 * @description 9 */ 10 public class GameRunnable implements Runnable { 11 @Override 12 public void run() { 13 for (int i = 0; i < 100; i++) { 14 System.out.println("=======打遊戲中============" + i); 15 } 16 } 17 }
1 package com.wfd360.thread.demo01; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/03 5:29 8 * @description 9 */ 10 public class MusicRunnable implements Runnable { 11 @Override 12 public void run() { 13 for (int i = 0; i < 100; i++) { 14 System.out.println("=======聽音樂中============"+i); 15 } 16 } 17 }
繼承Threadui
1 package com.wfd360.thread.demo02; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/03 6:00 8 * @description 9 */ 10 public class GameThread extends Thread { 11 @Override 12 public void run() { 13 for (int i = 0; i < 100; i++) { 14 System.out.println("-------遊戲中----------"+i); 15 } 16 } 17 }
1 package com.wfd360.thread.demo02; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/03 6:00 8 * @description 9 */ 10 public class MusicThread extends Thread { 11 @Override 12 public void run() { 13 for (int i = 0; i < 100; i++) { 14 System.out.println("-------音樂中----------" + i); 15 } 16 } 17 }
總結編碼
啓動線程兩種方式:
1.經過繼承Thread類
2.實現Runnable接口
使用哪一種方式更好?
區別:
一個類若是繼承了其餘類,就沒法在繼承Thread類,在Java中,一個類只能繼承一個類,而一個類若是實現了一個接口,還能夠實現其餘接口,接口是能夠多實現的,因此說
Runable的擴展性更強,可是繼承的方式更簡單,我的建議通常狀況下使用Thread;
實現接口Runnable或繼承Thread,經過看源碼你會發現Thread類實現了接口Runnable,使用本質上這兩種方法是同樣的
啓動線程流程:
建立啓動線程的方式一:繼承Thread類
1.將業務方法封裝成線程對象,自定義類t extends Thread類;
2.覆寫run方法: 覆寫第一步中的run方法;
3.建立自定義對象t
4.啓動線程 t.start();
建立啓動線程方式二:實現Runnable接口
1.將業務方法封裝成線程對象,自定義類t implements Runnable接口;
2.實現第一步中的run方法
3.建立自定義對象t
4.啓動線程 new Thread(t).start();
問題:
直接寫一個簡單的HelloWorld 程序,有沒有線程?
==>有一個主線程,在垃圾回收的時候,有gc 線程。
1 package com.wfd360.thread; 2 3 import org.junit.Test; 4 5 /** 6 * @author 姿式帝-博客園 7 * @address https://www.cnblogs.com/newAndHui/ 8 * @WeChat 851298348 9 * @create 05/04 11:09 10 * @description <p> 11 * 問題: 12 * 直接寫一個簡單的HelloWorld 程序,有沒有線程? 13 * ==>有一個主線程,在垃圾回收的時候,有gc 線程。 14 * 結論:一旦線程啓動起來以後就是獨立的,和建立環境沒有關係; 15 * 啓動線程不能直接調用run方法,必須調用start方法; 16 * </p> 17 */ 18 public class TestDemo02 { 19 /** 20 * 若是把建立線程放在循環語句的 下 面,會交替出現嗎 21 * ==>否,由於主線程執行完成後纔會啓動hello線程 22 * 23 * @throws Exception 24 */ 25 @Test 26 public void test1() throws Exception { 27 System.out.println("---test start-------"); 28 // 執行主線程 29 for (int i = 0; i < 100; i++) { 30 System.out.println("-----test1--------" + i); 31 } 32 // 啓動hello線程 33 new HelloThread().start(); 34 System.out.println("=======等待執行完成==========="); 35 Thread.sleep(5 * 1000); 36 System.out.println("---test end-------"); 37 } 38 39 /** 40 * 若是把建立線程放在循環語句的 上 面,會交替出現嗎 41 * ==>可能會,可能不會,可能出現for循環完以後,線程尚未啓動完; 42 * 43 * @throws Exception 44 */ 45 @Test 46 public void test2() throws Exception { 47 System.out.println("---test start-------"); 48 // 啓動hello線程 49 new HelloThread().start(); 50 // 執行主線程 51 for (int i = 0; i < 100; i++) { 52 System.out.println("-----test1--------" + i); 53 } 54 System.out.println("=======等待執行完成==========="); 55 Thread.sleep(5 * 1000); 56 System.out.println("---test end-------"); 57 } 58 59 /** 60 * 採用內部類的方式定義一個hello線程對象 61 */ 62 class HelloThread extends Thread { 63 @Override 64 public void run() { 65 for (int i = 0; i < 100; i++) { 66 System.out.println("-----HelloThread--------" + i); 67 } 68 } 69 } 70 }
結論:一旦線程啓動起來以後就是獨立的,和建立環境沒有關係;
啓動線程不能直接調用run方法,必須調用start方法;
package com.wfd360.thread; /** * @author 姿式帝-博客園 * @address https://www.cnblogs.com/newAndHui/ * @WeChat 851298348 * @create 05/04 11:34 * @description <p> * Thread類的方法: * static void sleep(long millis) 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行); * </p> */ public class TestSleep { /** * 作一個簡易倒計時,10秒鐘,控制檯每一秒輸出一個數字,如10,9,8,7.....0 */ public static void main(String[] args) throws Exception { System.out.println("---test start-------"); for (int i = 10; i >= 0; i--) { Thread.sleep(1 * 1000); System.out.println(i); } System.out.println("---test end-------"); } }
繼承方式
簡單需求:使用多線程模擬多窗口售票
1 package com.wfd360.thread.demo03Ticket; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/04 11:55 8 * @description <p> 9 * 模擬多線程售票 10 * </p> 11 */ 12 public class TicketThread extends Thread { 13 // 假定票老是100張 14 private static Integer num = 100; 15 16 @Override 17 public void run() { 18 // 只要有票就一直售票 19 while (num > 0) { 20 System.out.println("正在出售第" + num + "張票"); 21 --num; 22 } 23 System.out.println("===售票結束==="); 24 } 25 }
test
/** * 測試模擬三個窗口售票 * @throws InterruptedException */ @Test public void testTicketThread() throws InterruptedException { System.out.println("---test start-------"); // 模擬多3個窗口售票 TicketThread ticketThread1 = new TicketThread(); TicketThread ticketThread2 = new TicketThread(); TicketThread ticketThread3 = new TicketThread(); // 啓動線程售票 ticketThread1.start(); ticketThread2.start(); ticketThread3.start(); System.out.println("======等待售票============"); Thread.sleep(5 * 1000); System.out.println("---test end-------"); }
結果:
1.在售票過程當中不能區分售出的票是那個窗口售出的,解決經過線程名稱判斷
2.有重複售出的票(後面的線程同步解決)
解決第一個問題,設置獲取線程名稱,經過Thread對象裏面自帶的getName,setName方法
具體代碼
設置線程名稱
獲取線程名稱
上面講了繼承的方式獲取線程名稱,那麼實現接口Runnable的方式怎麼獲取設置勒
繼承Thread的方式,能夠經過getName的方式獲取當前線程的名稱?
那使用Runnable的方式,能經過getName獲取嘛?
getName方法是Thread類的,可是TicketThread如今並無繼承Thread類,而是實現了Runnable接口.
問題:若是實現Runnable接口,怎麼獲取線程名稱?
思考:TicketThread類裏面的代碼要執行,它確定存在於某個線程中, 就好比寫個helloword打印語句,是否是也處於一個主線程中,那這裏怎麼獲取線程名稱?
經過動態獲取,當程序正在執行的時候,獲取當前正在執行的線程名稱。怎麼獲取?
在Thread類裏面有個靜態的方法currentThread() 方法,返回當前正在執行的線程引用;
Thread.currentThread().getName
那怎麼設置線程名稱?
Thread類裏面有個name字段,至關於Thread類把它包裝了一下:
經過源碼能夠發現,構造方法裏面還有能夠傳一個名字:
具體實現代碼以下
總結:
繼承方式設置\獲取線程名稱經過 Thread對象裏面的 setName,getName方法;
實現接口方式設置名稱經過 new Thread('線程實例對象', "線程名稱"),獲取線程名稱經過:Thread.currentThread().getName
void join() 方法 :等待該線程終止
void join(long millis) 方法 :等待該線程終止的時間最長爲millis毫秒
需求: 當主線程運行到20的時候(i =20)的時候,讓JoinThread線程加進來直到執行完成,在執行主線程.
1 package com.wfd360.thread; 2 3 import org.junit.Test; 4 5 /** 6 * @author 姿式帝-博客園 7 * @address https://www.cnblogs.com/newAndHui/ 8 * @WeChat 851298348 9 * @create 05/04 6:31 10 * @description 11 */ 12 public class Test05Join { 13 /** 14 * 需求: 15 * 當主線程for循環到i=20時,等JoinThread線程執行完成後,在執行for循環的線程 16 * @throws InterruptedException 17 */ 18 @Test 19 public void testJoinThread() throws InterruptedException { 20 System.out.println("---test start-------"); 21 // 開啓線程 22 JoinThread thread = new JoinThread(); 23 thread.start(); 24 // 循環打印線程 25 for (int i = 0; i < 100; i++) { 26 System.out.println("======testJoinThread=========="+i); 27 Thread.sleep(1); 28 if (i==20){ 29 // 等線程JoinThread執行完成 30 thread.join(); 31 } 32 } 33 System.out.println("=============等待線程執行完成==================="); 34 Thread.sleep(10*1000); 35 System.out.println("---test end-------"); 36 } 37 38 class JoinThread extends Thread { 39 @Override 40 public void run() { 41 for (int i = 0; i < 100; i++) { 42 System.out.println("=====JoinThread=======" + i); 43 // 模擬處理不少業務耗時1毫秒 44 try { 45 Thread.sleep(1); 46 } catch (InterruptedException e) { 47 e.printStackTrace(); 48 } 49 } 50 } 51 } 52 }
直接上代碼
1 package com.wfd360.thread; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/05 8:17 8 * @description <p> 9 * 1.==>線程優先級的理解: 10 * 線程的優先級和生活中相似,高優先級線程的執行優先於低優先級線程; 11 * 並非絕對的,可能優先級高的線程優先 比 優先級低的線程先執行,只能說,高優先級的線程優先執行的概率更多; 12 * (好比兩個線程,一個優先級高,一個優先級低,若是一共運行一個小時,優先級高的線程執行遠遠大於優先級低的可是並非說優先級高的先執行完, 13 * 在執行優先級低的) 14 * 2.==>從新設置線程優先級 15 * int getPriority() 返回線程的優先級。 16 * void setPriority(int newPriority) 更改線程的優先級。Java線程的優先級從1到10級別,值越大優先級越高. 17 * 3.==>線程的默認優先級受建立線程的環境影響,默認值5,自定義線程的默認優先級和建立它的環境的線程優先級一致 18 * </p> 19 */ 20 public class Test06Priority { 21 /** 22 * 測試獲取線程優先級,設置線程優先級,驗證線程優先級受建立環境影響 23 * @param args 24 */ 25 public static void main(String[] args) { 26 Thread threadMain = Thread.currentThread(); 27 // 獲取默認優先級數字 28 System.out.println("main線程默認優先級:" +threadMain.getPriority());// 5 29 // 從新設置默認優先級數字 30 threadMain.setPriority(8); 31 // 再次從新獲取優先級數字 32 System.out.println("main線程修改後的優先級:" +threadMain.getPriority());// 8 33 // 建立一個線程查看優先級 34 Thread thread = new Thread(); 35 System.out.println("thread線程的優先級:" +thread.getPriority());// 8 受建立環境影響 36 } 37 }
直接看代碼
1 package com.wfd360.thread; 2 3 import com.wfd360.thread.demo04Daemon.DaemonThreaad; 4 5 /** 6 * @author 姿式帝-博客園 7 * @address https://www.cnblogs.com/newAndHui/ 8 * @WeChat 851298348 9 * @create 05/05 9:12 10 * @description <p> 11 * 後臺線程,即守護線程 12 * 後臺線程:指爲其餘線程提供服務的線程,也稱爲守護線程。JVM的垃圾回收線程就是一個後臺線程。 13 * 需求:嘗試把線程標記爲後臺線程或者標記爲(前臺)線程; 14 * Thread類提供的方法: 15 * 方法1: void setDaemon(boolean on) 將該線程標記爲守護線程或用戶線程,true爲後臺線程,false爲用戶線程(前臺線下) 16 * 怎樣測試該線程是不是守護線程? 17 * 方法2:isDaemon() 測試該線程是否爲守護線程. true爲後臺線程,false爲用戶線程(前臺線下) 18 * <p> 19 * 結論1:活動的線程(已經在執行的線程t.start())不能設置後臺線程,即主線程不能設置爲後臺線程。 20 * 結論2: 自定義線程的默認狀態和環境有關,後臺線程中建立的線程默認是後臺線程,前臺線程中建立的線程爲前臺線程. 21 * 結論3: 前臺線程執行完後,會直接關閉後臺線程,即自定義的後臺線程不必定能執行完成 22 * </p> 23 */ 24 public class Test07Daemon { 25 /** 26 * 測試1 27 * 查看主線程的狀態,嘗試更改 28 * 結論:活動的線程不能設置爲後臺線程 29 * 30 * @param args 31 */ 32 public static void main1(String[] args) { 33 Thread threadMain = Thread.currentThread(); 34 System.out.println("是後臺線程麼:" + threadMain.isDaemon());// false 35 threadMain.setDaemon(true); // 報錯,活動的線程不能設置爲後臺線程 36 System.out.println("修改後是後臺線程麼:" + threadMain.isDaemon()); 37 } 38 39 /** 40 * 測試2 41 * 查看主線程中 建立線程的狀態,嘗試更改; 42 * 43 * @param args 44 */ 45 public static void main2(String[] args) { 46 Thread thread = new Thread(); 47 // false 48 System.out.println("是後臺線程麼:" + thread.isDaemon()); 49 // 修改成後臺線程 50 thread.setDaemon(true); 51 System.out.println("修改後是後臺線程麼:" + thread.isDaemon()); 52 } 53 54 /** 55 * 測試3 56 * 查看主線程中 建立線程的狀態,嘗試更改,讓線程處於活動狀態在修改->報錯; 57 * 58 * @param args 59 */ 60 public static void main3(String[] args) { 61 DaemonThread thread = new DaemonThread(); 62 // 讓線程處於活躍狀態 63 thread.start(); 64 // false 65 System.out.println("是後臺線程麼:" + thread.isDaemon()); 66 // 修改成後臺線程,報錯,當前已是活躍狀態(thread.start())不能修改成後臺線程 67 thread.setDaemon(true); 68 System.out.println("修改後是後臺線程麼:" + thread.isDaemon()); 69 } 70 71 /** 72 * 測試4 73 * 前臺線程執行完後,會直接關閉後臺線程,即若是後臺線程不必定能執行完成 74 * 能夠經過修改等待執行時間來觀察DaemonThread線程的數組輸出變化 75 * 76 * @param args 77 */ 78 public static void main(String[] args) throws InterruptedException { 79 DaemonThread thread = new DaemonThread(); 80 // 修改成後臺線程 81 thread.setDaemon(true); 82 // 讓線程處於活躍狀態 83 thread.start(); 84 System.out.println("========等待後臺線程執行============"); 85 Thread.sleep(5 * 1000); 86 } 87 }
1 package com.wfd360.thread.demo04Daemon; 2 3 /** 4 * @author 姿式帝-博客園 5 * @address https://www.cnblogs.com/newAndHui/ 6 * @WeChat 851298348 7 * @create 05/05 9:27 8 * @description 9 */ 10 public class DaemonThread extends Thread { 11 @Override 12 public void run() { 13 for (int i = 0; i < 10; i++) { 14 System.out.println("===="+i); 15 try { 16 Thread.sleep(1000); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 } 21 } 22 }
線程基礎相關的方法定義就先到這裏,下一篇咱們將進入線程同步.
http://www.javashuo.com/article/p-dcimdnzm-bn.html
系統化的在線學習:點擊進入學習