您好,登錄后才能下訂單哦!
更多內(nèi)容請關(guān)注微信公眾號【Java技術(shù)江湖】
這是一位阿里 Java 工程師的技術(shù)小站,作者黃小斜,專注 Java 相關(guān)技術(shù):SSM、SpringBoot、MySQL、分布式、中間件、集群、Linux、網(wǎng)絡(luò)、多線程,偶爾講點(diǎn)Docker、ELK,同時(shí)也分享技術(shù)干貨和學(xué)習(xí)經(jīng)驗(yàn),致力于Java全棧開發(fā)?。P(guān)注公眾號后回復(fù)”資料“即可領(lǐng)取 3T 免費(fèi)技術(shù)學(xué)習(xí)資源以及我我原創(chuàng)的程序員校招指南、Java學(xué)習(xí)指南等資源)
cdn.xitu.io/2019/4/6/169f1735fd0d1d16?w=900&h=500&f=jpeg&s=109856">
本文介紹了Java多線程的基本概念,使用方法,以及底層實(shí)現(xiàn)原理。幫助你更好地使用Java的多線程。
具體代碼在我的GitHub中可以找到
https://github.com/h3pl/MyTech
喜歡的話麻煩點(diǎn)一下星哈謝謝。
文章首發(fā)于我的個(gè)人博客:
https://h3pl.github.io/2018/05/04/javase16
更多關(guān)于Java后端學(xué)習(xí)的內(nèi)容請到我的CSDN博客上查看:
https://blog.csdn.net/a724888
Java之父對線程的定義是:
線程是一個(gè)獨(dú)立執(zhí)行的調(diào)用序列,同一個(gè)進(jìn)程的線程在同一時(shí)刻共享一些系統(tǒng)資源(比如文件句柄等)也能訪問同一個(gè)進(jìn)程所創(chuàng)建的對象資源(內(nèi)存資源)。java.lang.Thread對象負(fù)責(zé)統(tǒng)計(jì)和控制這種行為。
每個(gè)程序都至少擁有一個(gè)線程-即作為Java虛擬機(jī)(JVM)啟動參數(shù)運(yùn)行在主類main方法的線程。在Java虛擬機(jī)初始化過程中也可能啟動其他的后臺線程。這種線程的數(shù)目和種類因JVM的實(shí)現(xiàn)而異。然而所有用戶級線程都是顯式被構(gòu)造并在主線程或者是其他用戶線程中被啟動。
本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。在這之前,首先讓我們來了解下在操作系統(tǒng)中進(jìn)程和線程的區(qū)別:   進(jìn)程:每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(進(jìn)程上下文),進(jìn)程間的切換會有較大的開銷,一個(gè)進(jìn)程包含1--n個(gè)線程。(進(jìn)程是資源分配的最小單位)   線程:同一類線程共享代碼和數(shù)據(jù)空間,每個(gè)線程有獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器(PC),線程切換開銷小。(線程是cpu調(diào)度的最小單位)   線程和進(jìn)程一樣分為五個(gè)階段:創(chuàng)建、就緒、運(yùn)行、阻塞、終止。   多進(jìn)程是指操作系統(tǒng)能同時(shí)運(yùn)行多個(gè)任務(wù)(程序)。   多線程是指在同一程序中有多個(gè)順序流在執(zhí)行。 在java中要想實(shí)現(xiàn)多線程,有兩種手段,一種是繼續(xù)Thread類,另外一種是實(shí)現(xiàn)Runable接口.(其實(shí)準(zhǔn)確來講,應(yīng)該有三種,還有一種是實(shí)現(xiàn)Callable接口,并與Future、線程池結(jié)合使用
下面的圖大致介紹了Java線程的調(diào)用過程,每個(gè)線程使用一個(gè)獨(dú)立的調(diào)用棧進(jìn)行線程執(zhí)行,棧中的數(shù)據(jù)不共享,堆區(qū)和方法區(qū)的數(shù)據(jù)是共享的。
構(gòu)造方法 Thread類中不同的構(gòu)造方法接受如下參數(shù)的不同組合: 一個(gè)Runnable對象,這種情況下,Thread.start方法將會調(diào)用對應(yīng)Runnable對象的run方法。如果沒有提供Runnable對象,那么就會立即得到一個(gè)Thread.run的默認(rèn)實(shí)現(xiàn)。 一個(gè)作為線程標(biāo)識名的String字符串,該標(biāo)識在跟蹤和調(diào)試過程中會非常有用,除此別無它用。 線程組(ThreadGroup),用來放置新創(chuàng)建的線程,如果提供的ThreadGroup不允許被訪問,那么就會拋出一個(gè)SecurityException 。 Thread對象擁有一個(gè)守護(hù)(daemon)標(biāo)識屬性,這個(gè)屬性無法在構(gòu)造方法中被賦值,但是可以在線程啟動之前設(shè)置該屬性(通過setDaemon方法)。 當(dāng)程序中所有的非守護(hù)線程都已經(jīng)終止,調(diào)用setDaemon方法可能會導(dǎo)致虛擬機(jī)粗暴的終止線程并退出。 isDaemon方法能夠返回該屬性的值。守護(hù)狀態(tài)的作用非常有限,即使是后臺線程在程序退出的時(shí)候也經(jīng)常需要做一些清理工作。 (daemon的發(fā)音為”day-mon”,這是系統(tǒng)編程傳統(tǒng)的遺留,系統(tǒng)守護(hù)進(jìn)程是一個(gè)持續(xù)運(yùn)行的進(jìn)程,比如打印機(jī)隊(duì)列管理,它總是在系統(tǒng)中運(yùn)行。)
啟動線程 調(diào)用start方法會觸發(fā)Thread實(shí)例以一個(gè)新的線程啟動其run方法。新線程不會持有調(diào)用線程的任何同步鎖。
當(dāng)一個(gè)線程正常地運(yùn)行結(jié)束或者拋出某種未檢測的異常(比如,運(yùn)行時(shí)異常(RuntimeException),錯(cuò)誤(ERROR) 或者其子類)線程就會終止。
當(dāng)線程終止之后,是不能被重新啟動的。在同一個(gè)Thread上調(diào)用多次start方法會拋出InvalidThreadStateException異常。
如果線程已經(jīng)啟動但是還沒有終止,那么調(diào)用isAlive方法就會返回true.即使線程由于某些原因處于阻塞(Blocked)狀態(tài)該方法依然返回true。
如果線程已經(jīng)被取消(cancelled),那么調(diào)用其isAlive在什么時(shí)候返回false就因各Java虛擬機(jī)的實(shí)現(xiàn)而異了。沒有方法可以得知一個(gè)處于非活動狀態(tài)的線程是否已經(jīng)被啟動過了。
Java的線程實(shí)現(xiàn)基本上都是內(nèi)核級線程的實(shí)現(xiàn),所以Java線程的具體執(zhí)行還取決于操作系統(tǒng)的特性。
Java虛擬機(jī)為了實(shí)現(xiàn)跨平臺(不同的硬件平臺和各種操作系統(tǒng))的特性,Java語言在線程調(diào)度與調(diào)度公平性上未作出任何的承諾,甚至都不會嚴(yán)格保證線程會被執(zhí)行。但是Java線程卻支持優(yōu)先級的方法,這些方法會影響線程的調(diào)度:
每個(gè)線程都有一個(gè)優(yōu)先級,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之間(分別為1和10) 默認(rèn)情況下,新創(chuàng)建的線程都擁有和創(chuàng)建它的線程相同的優(yōu)先級。main方法所關(guān)聯(lián)的初始化線程擁有一個(gè)默認(rèn)的優(yōu)先級,這個(gè)優(yōu)先級是Thread.NORM_PRIORITY (5).
線程的當(dāng)前優(yōu)先級可以通過getPriority方法獲得。 線程的優(yōu)先級可以通過setPriority方法來動態(tài)的修改,一個(gè)線程的最高優(yōu)先級由其所在的線程組限定。
只有很少幾個(gè)方法可以用于跨線程交流:
每個(gè)線程都有一個(gè)相關(guān)的Boolean類型的中斷標(biāo)識。在線程t上調(diào)用t.interrupt會將該線程的中斷標(biāo)識設(shè)為true,除非線程t正處于Object.wait,Thread.sleep,或者Thread.join,這些情況下interrupt調(diào)用會導(dǎo)致t上的這些操作拋出InterruptedException異常,但是t的中斷標(biāo)識會被設(shè)為false。 任何一個(gè)線程的中斷狀態(tài)都可以通過調(diào)用isInterrupted方法來得到。如果線程已經(jīng)通過interrupt方法被中斷,這個(gè)方法將會返回true。 但是如果調(diào)用了Thread.interrupted方法且中斷標(biāo)識還沒有被重置,或者是線程處于wait,sleep,join過程中,調(diào)用isInterrupted方法將會拋出InterruptedException異常。 調(diào)用t.join()方法將會暫停執(zhí)行調(diào)用線程,直到線程t執(zhí)行完畢:當(dāng)t.isAlive()方法返回false的時(shí)候調(diào)用t.join()將會直接返回(return)。 另一個(gè)帶參數(shù)毫秒(millisecond)的join方法在被調(diào)用時(shí),如果線程沒能夠在指定的時(shí)間內(nèi)完成,調(diào)用線程將重新得到控制權(quán)。 因?yàn)閕sAlive方法的實(shí)現(xiàn)原理,所以在一個(gè)還沒有啟動的線程上調(diào)用join方法是沒有任何意義的。同樣的,試圖在一個(gè)還沒有創(chuàng)建的線程上調(diào)用join方法也是不明智的。 起初,Thread類還支持一些另外一些控制方法:suspend,resume,stop以及destroy。這幾個(gè)方法已經(jīng)被聲明過期。其中destroy方法從來沒有被實(shí)現(xiàn),估計(jì)以后也不會。而通過使用等待/喚醒機(jī)制增加suspend和resume方法在安全性和可靠性的效果有所欠缺
靜態(tài)方法 Thread類中的部分方法被設(shè)計(jì)為只適用于當(dāng)前正在運(yùn)行的線程(即調(diào)用Thread方法的線程)。為強(qiáng)調(diào)這點(diǎn),這些方法都被聲明為靜態(tài)的。 Thread.currentThread方法會返回當(dāng)前線程的引用,得到這個(gè)引用可以用來調(diào)用其他的非靜態(tài)方法,比如Thread.currentThread().getPriority()會返回調(diào)用線程的優(yōu)先級。 Thread.interrupted方法會清除當(dāng)前線程的中斷狀態(tài)并返回前一個(gè)狀態(tài)。(一個(gè)線程的中斷狀態(tài)是不允許被其他線程清除的) Thread.sleep(long msecs)方法會使得當(dāng)前線程暫停執(zhí)行至少msecs毫秒。 Thread.yield方法純粹只是建議Java虛擬機(jī)對其他已經(jīng)處于就緒狀態(tài)的線程(如果有的話)調(diào)度執(zhí)行,而不是當(dāng)前線程。最終Java虛擬機(jī)如何去實(shí)現(xiàn)這種行為就完全看其喜好了。
每一個(gè)線程都是一個(gè)線程組中的成員。默認(rèn)情況下,新建線程和創(chuàng)建它的線程屬于同一個(gè)線程組。線程組是以樹狀分布的。 當(dāng)創(chuàng)建一個(gè)新的線程組,這個(gè)線程組成為當(dāng)前線程組的子組。getThreadGroup方法會返回當(dāng)前線程所屬的線程組,對應(yīng)地,ThreadGroup類也有方法可以得到哪些線程目前屬于這個(gè)線程組,比如enumerate方法。 ThreadGroup類存在的一個(gè)目的是支持安全策略來動態(tài)的限制對該組的線程操作。比如對不屬于同一組的線程調(diào)用interrupt是不合法的。 這是為避免某些問題(比如,一個(gè)applet線程嘗試殺掉主屏幕的刷新線程)所采取的措施。ThreadGroup也可以為該組所有線程設(shè)置一個(gè)最大的線程優(yōu)先級。 線程組往往不會直接在程序中被使用。在大多數(shù)的應(yīng)用中,如果僅僅是為在程序中跟蹤線程對象的分組,那么普通的集合類(比如java.util.Vector)應(yīng)是更好的選擇。
public class 多線程實(shí)例 { //繼承thread @Test public void test1() { class A extends Thread { @Override public void run() { System.out.println("A run"); } } A a = new A(); a.start(); } //實(shí)現(xiàn)Runnable @Test public void test2() { class B implements Runnable { @Override public void run() { System.out.println("B run"); } } B b = new B(); //Runable實(shí)現(xiàn)類需要由Thread類包裝后才能執(zhí)行 new Thread(b).start(); } //有返回值的線程 @Test public void test3() { Callable callable = new Callable() { int sum = 0; @Override public Object call() throws Exception { for (int i = 0;i < 5;i ++) { sum += i; } return sum; } }; //這里要用FutureTask,否則不能加入Thread構(gòu)造方法 FutureTask futureTask = new FutureTask(callable); new Thread(futureTask).start(); try { System.out.println(futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } //線程池實(shí)現(xiàn) @Test public void test4() { ExecutorService executorService = Executors.newFixedThreadPool(5); //execute直接執(zhí)行線程 executorService.execute(new Thread()); executorService.execute(new Runnable() { @Override public void run() { System.out.println("runnable"); } }); //submit提交有返回結(jié)果的任務(wù),運(yùn)行完后返回結(jié)果。 Future future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "a"; } }); try { System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } ArrayList<String> list = new ArrayList<>(); //有返回值的線程組將返回值存進(jìn)集合 for (int i = 0;i < 5;i ++ ) { int finalI = i; Future future1 = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "res" + finalI; } }); try { list.add((String) future1.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } for (String s : list) { System.out.println(s); } } }
public class 線程的狀態(tài)轉(zhuǎn)換 { //一開始線程是init狀態(tài),結(jié)束時(shí)是terminated狀態(tài) class t implements Runnable { private String name; public t(String name) { this.name = name; } @Override public void run() { System.out.println(name + "run"); } } //測試join,父線程在子線程運(yùn)行時(shí)進(jìn)入waiting狀態(tài) @Test public void test1() throws InterruptedException { Thread dad = new Thread(new Runnable() { Thread son = new Thread(new t("son")); @Override public void run() { System.out.println("dad init"); son.start(); try { //保證子線程運(yùn)行完再運(yùn)行父線程 son.join(); System.out.println("dad run"); } catch (InterruptedException e) { e.printStackTrace(); } } }); //調(diào)用start,線程進(jìn)入runnable狀態(tài),等待系統(tǒng)調(diào)度 dad.start(); //在父線程中對子線程實(shí)例使用join,保證子線程在父線程之前執(zhí)行完 } //測試sleep @Test public void test2(){ Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("t1 run"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); //主線程休眠。進(jìn)入time waiting狀態(tài) try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } t1.start(); } //線程2進(jìn)入blocked狀態(tài)。 public static void main(String[] args) { test4(); Thread.yield();//進(jìn)入runnable狀態(tài) } //測試blocked狀態(tài) public static void test4() { class A { //線程1獲得實(shí)例鎖以后線程2無法獲得實(shí)例鎖,所以進(jìn)入blocked狀態(tài) synchronized void run() { while (true) { System.out.println("run"); } } } A a = new A(); new Thread(new Runnable() { @Override public void run() { System.out.println("t1 get lock"); a.run(); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println("t2 get lock"); a.run(); } }).start(); } //volatile保證線程可見性 volatile static int flag = 1; //object作為鎖對象,用于線程使用wait和notify方法 volatile static Object o = new Object(); //測試wait和notify //wait后進(jìn)入waiting狀態(tài),被notify進(jìn)入blocked(阻塞等待鎖釋放)或者runnable狀態(tài)(獲取到鎖) public void test5() { new Thread(new Runnable() { @Override public void run() { //wait和notify只能在同步代碼塊內(nèi)使用 synchronized (o) { while (true) { if (flag == 0) { try { Thread.sleep(2000); System.out.println("thread1 wait"); //釋放鎖,線程掛起進(jìn)入object的等待隊(duì)列,后續(xù)代碼運(yùn)行 o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread1 run"); System.out.println("notify t2"); flag = 0; //通知等待隊(duì)列的一個(gè)線程獲取鎖 o.notify(); } } } }).start(); //解釋同上 new Thread(new Runnable() { @Override public void run() { while (true) { synchronized (o) { if (flag == 1) { try { Thread.sleep(2000); System.out.println("thread2 wait"); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread2 run"); System.out.println("notify t1"); flag = 1; o.notify(); } } } }).start(); } //輸出結(jié)果是 // thread1 run // notify t2 // thread1 wait // thread2 run // notify t1 // thread2 wait // thread1 run // notify t2 //不斷循環(huán) }
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。