本篇內(nèi)容介紹了“Java多線程使用方式和實現(xiàn)原理”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
成都創(chuàng)新互聯(lián)公司堅持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站制作、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時代的叢臺網(wǎng)站設(shè)計、移動媒體設(shè)計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
Java之父對線程的定義是:
線程是一個獨立執(zhí)行的調(diào)用序列,同一個進程的線程在同一時刻共享一些系統(tǒng)資源(比如文件句柄等)也能訪問同一個進程所創(chuàng)建的對象資源(內(nèi)存資源)。java.lang.Thread對象負責(zé)統(tǒng)計和控制這種行為。
每個程序都至少擁有一個線程-即作為Java虛擬機(JVM)啟動參數(shù)運行在主類main方法的線程。在Java虛擬機初始化過程中也可能啟動其他的后臺線程。這種線程的數(shù)目和種類因JVM的實現(xiàn)而異。然而所有用戶級線程都是顯式被構(gòu)造并在主線程或者是其他用戶線程中被啟動。
本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。在這之前,首先讓我們來了解下在操作系統(tǒng)中進程和線程的區(qū)別: 進程:每個進程都有獨立的代碼和數(shù)據(jù)空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1--n個線程。(進程是資源分配的最小單位) 線程:同一類線程共享代碼和數(shù)據(jù)空間,每個線程有獨立的運行棧和程序計數(shù)器(PC),線程切換開銷小。(線程是cpu調(diào)度的最小單位) 線程和進程一樣分為五個階段:創(chuàng)建、就緒、運行、阻塞、終止。 多進程是指操作系統(tǒng)能同時運行多個任務(wù)(程序)。 多線程是指在同一程序中有多個順序流在執(zhí)行。 在java中要想實現(xiàn)多線程,有兩種手段,一種是繼續(xù)Thread類,另外一種是實現(xiàn)Runable接口.(其實準(zhǔn)確來講,應(yīng)該有三種,還有一種是實現(xiàn)Callable接口,并與Future、線程池結(jié)合使用
Java 給多線程編程提供了內(nèi)置的支持。 一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發(fā)多個線程,每條線程并行執(zhí)行不同的任務(wù)。
多線程是多任務(wù)的一種特別的形式,但多線程使用了更小的資源開銷。
這里定義和線程相關(guān)的另一個術(shù)語 - 進程:一個進程包括由操作系統(tǒng)分配的內(nèi)存空間,包含一個或多個線程。一個線程不能獨立的存在,它必須是進程的一部分。一個進程一直運行,直到所有的非守護線程都結(jié)束運行后才能結(jié)束。
多線程能滿足程序員編寫高效率的程序來達到充分利用 CPU 的目的。
線程是一個動態(tài)執(zhí)行的過程,它也有一個從產(chǎn)生到死亡的過程。
下圖顯示了一個線程完整的生命周期。
新建狀態(tài):
使用 new關(guān)鍵字和 Thread類或其子類建立一個線程對象后,該線程對象就處于新建狀態(tài)。它保持這個狀態(tài)直到程序 start()這個線程。
就緒狀態(tài):
當(dāng)線程對象調(diào)用了start()方法之后,該線程就進入就緒狀態(tài)。就緒狀態(tài)的線程處于就緒隊列中,要等待JVM里線程調(diào)度器的調(diào)度。
運行狀態(tài):
如果就緒狀態(tài)的線程獲取 CPU 資源,就可以執(zhí)行 run(),此時線程便處于運行狀態(tài)。處于運行狀態(tài)的線程最為復(fù)雜,它可以變?yōu)樽枞麪顟B(tài)、就緒狀態(tài)和死亡狀態(tài)。
阻塞狀態(tài):
如果一個線程執(zhí)行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線程就從運行狀態(tài)進入阻塞狀態(tài)。在睡眠時間已到或獲得設(shè)備資源后可以重新進入就緒狀態(tài)??梢苑譃槿N:
等待阻塞:運行狀態(tài)中的線程執(zhí)行 wait() 方法,使線程進入到等待阻塞狀態(tài)。
同步阻塞:線程在獲取 synchronized 同步鎖失敗(因為同步鎖被其他線程占用)。
其他阻塞:通過調(diào)用線程的 sleep() 或 join() 發(fā)出了 I/O 請求時,線程就會進入到阻塞狀態(tài)。當(dāng)sleep() 狀態(tài)超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程重新轉(zhuǎn)入就緒狀態(tài)。
死亡狀態(tài):
一個運行狀態(tài)的線程完成任務(wù)或者其他終止條件發(fā)生時,該線程就切換到終止?fàn)顟B(tài)。
public class 多線程實例 {
//繼承thread @Test public void test1() { class A extends Thread { @Override public void run() { System.out.println("A run"); } } A a = new A(); a.start(); } //實現(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實現(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(); } } //線程池實現(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ù),運行完后返回結(jié)果。 Future future = executorService.submit(new Callable() { @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 list = new ArrayList<>(); //有返回值的線程組將返回值存進集合 for (int i = 0;i < 5;i ++ ) { int finalI = i; Future future1 = executorService.submit(new Callable () { @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é)束時是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,父線程在子線程運行時進入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 { //保證子線程運行完再運行父線程 son.join(); System.out.println("dad run"); } catch (InterruptedException e) { e.printStackTrace(); } } }); //調(diào)用start,線程進入runnable狀態(tài),等待系統(tǒng)調(diào)度 dad.start(); //在父線程中對子線程實例使用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(); } } }); //主線程休眠。進入time waiting狀態(tài) try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } t1.start(); } //線程2進入blocked狀態(tài)。 public static void main(String[] args) { test4(); Thread.yield();//進入runnable狀態(tài) } //測試blocked狀態(tài) public static void test4() { class A { //線程1獲得實例鎖以后線程2無法獲得實例鎖,所以進入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后進入waiting狀態(tài),被notify進入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"); //釋放鎖,線程掛起進入object的等待隊列,后續(xù)代碼運行 o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread1 run"); System.out.println("notify t2"); flag = 0; //通知等待隊列的一個線程獲取鎖 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) }
執(zhí)行此方法會向系統(tǒng)線程調(diào)度器(Schelduler)發(fā)出一個暗示,告訴其當(dāng)前JAVA線程打算放棄對CPU的使用,但該暗示,有可能被調(diào)度器忽略。使用該方法,可以防止線程對CPU的過度使用,提高系統(tǒng)性能。
Thread#sleep(time)或Thread.sleep(time, nanos):
使當(dāng)前線程進入休眠階段,狀態(tài)變?yōu)椋篢IME_WAITING
中斷當(dāng)前線程的執(zhí)行,允許當(dāng)前線程對自身進行中斷,否則將會校驗調(diào)用方線程是否有對該線程的權(quán)限。
如果當(dāng)前線程因被調(diào)用Object#wait(),Object#wait(long, int), 或者線程本身的join(), join(long),sleep()處于阻塞狀態(tài)中,此時調(diào)用interrupt方法會使拋出InterruptedException,而且線程的阻塞狀態(tài)將會被清除。
查看當(dāng)前線程是否處于中斷狀態(tài),這個方法比較特殊之處在于,如果調(diào)用成功,會將當(dāng)前線程的interrupt status清除。所以如果連續(xù)2次調(diào)用該方法,第二次將返回false。
與上面方法相同的地方在于,該方法返回當(dāng)前線程的中斷狀態(tài)。不同的地方在于,它不會清除當(dāng)前線程的interrupt status狀態(tài)。
A線程調(diào)用B線程的join()方法,將會使A等待B執(zhí)行,直到B線程終止。如果傳入time參數(shù),將會使A等待B執(zhí)行time的時間,如果time時間到達,將會切換進A線程,繼續(xù)執(zhí)行A線程。
構(gòu)造方法 Thread類中不同的構(gòu)造方法接受如下參數(shù)的不同組合: 一個Runnable對象,這種情況下,Thread.start方法將會調(diào)用對應(yīng)Runnable對象的run方法。如果沒有提供Runnable對象,那么就會立即得到一個Thread.run的默認實現(xiàn)。 一個作為線程標(biāo)識名的String字符串,該標(biāo)識在跟蹤和調(diào)試過程中會非常有用,除此別無它用。 線程組(ThreadGroup),用來放置新創(chuàng)建的線程,如果提供的ThreadGroup不允許被訪問,那么就會拋出一個SecurityException 。 Thread對象擁有一個守護(daemon)標(biāo)識屬性,這個屬性無法在構(gòu)造方法中被賦值,但是可以在線程啟動之前設(shè)置該屬性(通過setDaemon方法)。 當(dāng)程序中所有的非守護線程都已經(jīng)終止,調(diào)用setDaemon方法可能會導(dǎo)致虛擬機粗暴的終止線程并退出。 isDaemon方法能夠返回該屬性的值。守護狀態(tài)的作用非常有限,即使是后臺線程在程序退出的時候也經(jīng)常需要做一些清理工作。 (daemon的發(fā)音為”day-mon”,這是系統(tǒng)編程傳統(tǒng)的遺留,系統(tǒng)守護進程是一個持續(xù)運行的進程,比如打印機隊列管理,它總是在系統(tǒng)中運行。)
啟動線程
調(diào)用start方法會觸發(fā)Thread實例以一個新的線程啟動其run方法。新線程不會持有調(diào)用線程的任何同步鎖。
當(dāng)一個線程正常地運行結(jié)束或者拋出某種未檢測的異常(比如,運行時異常(RuntimeException),錯誤(ERROR) 或者其子類)線程就會終止。
當(dāng)線程終止之后,是不能被重新啟動的。在同一個Thread上調(diào)用多次start方法會拋出InvalidThreadStateException異常。
如果線程已經(jīng)啟動但是還沒有終止,那么調(diào)用isAlive方法就會返回true.即使線程由于某些原因處于阻塞(Blocked)狀態(tài)該方法依然返回true。
如果線程已經(jīng)被取消(cancelled),那么調(diào)用其isAlive在什么時候返回false就因各Java虛擬機的實現(xiàn)而異了。沒有方法可以得知一個處于非活動狀態(tài)的線程是否已經(jīng)被啟動過了。
Java的線程實現(xiàn)基本上都是內(nèi)核級線程的實現(xiàn),所以Java線程的具體執(zhí)行還取決于操作系統(tǒng)的特性。
Java虛擬機為了實現(xiàn)跨平臺(不同的硬件平臺和各種操作系統(tǒng))的特性,Java語言在線程調(diào)度與調(diào)度公平性上未作出任何的承諾,甚至都不會嚴(yán)格保證線程會被執(zhí)行。但是Java線程卻支持優(yōu)先級的方法,這些方法會影響線程的調(diào)度:
每個線程都有一個優(yōu)先級,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之間(分別為1和10)
默認情況下,新創(chuàng)建的線程都擁有和創(chuàng)建它的線程相同的優(yōu)先級。main方法所關(guān)聯(lián)的初始化線程擁有一個默認的優(yōu)先級,這個優(yōu)先級是Thread.NORM_PRIORITY (5).
線程的當(dāng)前優(yōu)先級可以通過getPriority方法獲得。
線程的優(yōu)先級可以通過setPriority方法來動態(tài)的修改,一個線程的最高優(yōu)先級由其所在的線程組限定。
“Java多線程使用方式和實現(xiàn)原理”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!