您好,登錄后才能下訂單哦!
本篇內容介紹了“Java多線程使用方式和實現原理”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
Java之父對線程的定義是:
線程是一個獨立執行的調用序列,同一個進程的線程在同一時刻共享一些系統資源(比如文件句柄等)也能訪問同一個進程所創建的對象資源(內存資源)。java.lang.Thread對象負責統計和控制這種行為。
每個程序都至少擁有一個線程-即作為Java虛擬機(JVM)啟動參數運行在主類main方法的線程。在Java虛擬機初始化過程中也可能啟動其他的后臺線程。這種線程的數目和種類因JVM的實現而異。然而所有用戶級線程都是顯式被構造并在主線程或者是其他用戶線程中被啟動。
本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。在這之前,首先讓我們來了解下在操作系統中進程和線程的區別: 進程:每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1--n個線程。(進程是資源分配的最小單位) 線程:同一類線程共享代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。(線程是cpu調度的最小單位) 線程和進程一樣分為五個階段:創建、就緒、運行、阻塞、終止。 多進程是指操作系統能同時運行多個任務(程序)。 多線程是指在同一程序中有多個順序流在執行。 在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另外一種是實現Runable接口.(其實準確來講,應該有三種,還有一種是實現Callable接口,并與Future、線程池結合使用
Java 給多線程編程提供了內置的支持。 一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并行執行不同的任務。
多線程是多任務的一種特別的形式,但多線程使用了更小的資源開銷。
這里定義和線程相關的另一個術語 - 進程:一個進程包括由操作系統分配的內存空間,包含一個或多個線程。一個線程不能獨立的存在,它必須是進程的一部分。一個進程一直運行,直到所有的非守護線程都結束運行后才能結束。
多線程能滿足程序員編寫高效率的程序來達到充分利用 CPU 的目的。
線程是一個動態執行的過程,它也有一個從產生到死亡的過程。
下圖顯示了一個線程完整的生命周期。
新建狀態:
使用 new 關鍵字和 Thread 類或其子類建立一個線程對象后,該線程對象就處于新建狀態。它保持這個狀態直到程序 start() 這個線程。
就緒狀態:
當線程對象調用了start()方法之后,該線程就進入就緒狀態。就緒狀態的線程處于就緒隊列中,要等待JVM里線程調度器的調度。
運行狀態:
如果就緒狀態的線程獲取 CPU 資源,就可以執行 run(),此時線程便處于運行狀態。處于運行狀態的線程最為復雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。
阻塞狀態:
如果一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或獲得設備資源后可以重新進入就緒狀態。可以分為三種:
等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。
同步阻塞:線程在獲取 synchronized 同步鎖失敗(因為同步鎖被其他線程占用)。
其他阻塞:通過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程重新轉入就緒狀態。
死亡狀態:
一個運行狀態的線程完成任務或者其他終止條件發生時,該線程就切換到終止狀態。
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(); } //實現Runnable @Test public void test2() { class B implements Runnable { @Override public void run() { System.out.println("B run"); } } B b = new B(); //Runable實現類需要由Thread類包裝后才能執行 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構造方法 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(); } } //線程池實現 @Test public void test4() { ExecutorService executorService = Executors.newFixedThreadPool(5); //execute直接執行線程 executorService.execute(new Thread()); executorService.execute(new Runnable() { @Override public void run() { System.out.println("runnable"); } }); //submit提交有返回結果的任務,運行完后返回結果。 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<>(); //有返回值的線程組將返回值存進集合 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 線程的狀態轉換 { //一開始線程是init狀態,結束時是terminated狀態 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狀態 @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(); } } }); //調用start,線程進入runnable狀態,等待系統調度 dad.start(); //在父線程中對子線程實例使用join,保證子線程在父線程之前執行完 } //測試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狀態 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } t1.start(); } //線程2進入blocked狀態。 public static void main(String[] args) { test4(); Thread.yield();//進入runnable狀態 } //測試blocked狀態 public static void test4() { class A { //線程1獲得實例鎖以后線程2無法獲得實例鎖,所以進入blocked狀態 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狀態,被notify進入blocked(阻塞等待鎖釋放)或者runnable狀態(獲取到鎖) public void test5() { new Thread(new Runnable() { @Override public void run() { //wait和notify只能在同步代碼塊內使用 synchronized (o) { while (true) { if (flag == 0) { try { Thread.sleep(2000); System.out.println("thread1 wait"); //釋放鎖,線程掛起進入object的等待隊列,后續代碼運行 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(); } //輸出結果是 // thread1 run // notify t2 // thread1 wait // thread2 run // notify t1 // thread2 wait // thread1 run // notify t2 //不斷循環 }
執行此方法會向系統線程調度器(Schelduler)發出一個暗示,告訴其當前JAVA線程打算放棄對CPU的使用,但該暗示,有可能被調度器忽略。使用該方法,可以防止線程對CPU的過度使用,提高系統性能。
Thread#sleep(time)或Thread.sleep(time, nanos):
使當前線程進入休眠階段,狀態變為:TIME_WAITING
中斷當前線程的執行,允許當前線程對自身進行中斷,否則將會校驗調用方線程是否有對該線程的權限。
如果當前線程因被調用Object#wait(),Object#wait(long, int), 或者線程本身的join(), join(long),sleep()處于阻塞狀態中,此時調用interrupt方法會使拋出InterruptedException,而且線程的阻塞狀態將會被清除。
查看當前線程是否處于中斷狀態,這個方法比較特殊之處在于,如果調用成功,會將當前線程的interrupt status清除。所以如果連續2次調用該方法,第二次將返回false。
與上面方法相同的地方在于,該方法返回當前線程的中斷狀態。不同的地方在于,它不會清除當前線程的interrupt status狀態。
A線程調用B線程的join()方法,將會使A等待B執行,直到B線程終止。如果傳入time參數,將會使A等待B執行time的時間,如果time時間到達,將會切換進A線程,繼續執行A線程。
構造方法 Thread類中不同的構造方法接受如下參數的不同組合: 一個Runnable對象,這種情況下,Thread.start方法將會調用對應Runnable對象的run方法。如果沒有提供Runnable對象,那么就會立即得到一個Thread.run的默認實現。 一個作為線程標識名的String字符串,該標識在跟蹤和調試過程中會非常有用,除此別無它用。 線程組(ThreadGroup),用來放置新創建的線程,如果提供的ThreadGroup不允許被訪問,那么就會拋出一個SecurityException 。 Thread對象擁有一個守護(daemon)標識屬性,這個屬性無法在構造方法中被賦值,但是可以在線程啟動之前設置該屬性(通過setDaemon方法)。 當程序中所有的非守護線程都已經終止,調用setDaemon方法可能會導致虛擬機粗暴的終止線程并退出。 isDaemon方法能夠返回該屬性的值。守護狀態的作用非常有限,即使是后臺線程在程序退出的時候也經常需要做一些清理工作。 (daemon的發音為”day-mon”,這是系統編程傳統的遺留,系統守護進程是一個持續運行的進程,比如打印機隊列管理,它總是在系統中運行。)
啟動線程
調用start方法會觸發Thread實例以一個新的線程啟動其run方法。新線程不會持有調用線程的任何同步鎖。
當一個線程正常地運行結束或者拋出某種未檢測的異常(比如,運行時異常(RuntimeException),錯誤(ERROR) 或者其子類)線程就會終止。
當線程終止之后,是不能被重新啟動的。在同一個Thread上調用多次start方法會拋出InvalidThreadStateException異常。
如果線程已經啟動但是還沒有終止,那么調用isAlive方法就會返回true.即使線程由于某些原因處于阻塞(Blocked)狀態該方法依然返回true。
如果線程已經被取消(cancelled),那么調用其isAlive在什么時候返回false就因各Java虛擬機的實現而異了。沒有方法可以得知一個處于非活動狀態的線程是否已經被啟動過了。
Java的線程實現基本上都是內核級線程的實現,所以Java線程的具體執行還取決于操作系統的特性。
Java虛擬機為了實現跨平臺(不同的硬件平臺和各種操作系統)的特性,Java語言在線程調度與調度公平性上未作出任何的承諾,甚至都不會嚴格保證線程會被執行。但是Java線程卻支持優先級的方法,這些方法會影響線程的調度:
每個線程都有一個優先級,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之間(分別為1和10)
默認情況下,新創建的線程都擁有和創建它的線程相同的優先級。main方法所關聯的初始化線程擁有一個默認的優先級,這個優先級是Thread.NORM_PRIORITY (5).
線程的當前優先級可以通過getPriority方法獲得。
線程的優先級可以通過setPriority方法來動態的修改,一個線程的最高優先級由其所在的線程組限定。
“Java多線程使用方式和實現原理”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。