26_多線程_第1天(Thread、線程創(chuàng)建、線程池)_講義_第1頁
26_多線程_第1天(Thread、線程創(chuàng)建、線程池)_講義_第2頁
26_多線程_第1天(Thread、線程創(chuàng)建、線程池)_講義_第3頁
26_多線程_第1天(Thread、線程創(chuàng)建、線程池)_講義_第4頁
26_多線程_第1天(Thread、線程創(chuàng)建、線程池)_講義_第5頁
已閱讀5頁,還剩11頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

傳智播客專注于Java、.Net和Php、網頁平面設計工程師的培訓第26天 多線程今日內容介紹u Threadu 線程創(chuàng)建u 線程池u 線程狀態(tài)圖第1章 多線程1.1 多線程介紹學習多線程之前,我們先要了解幾個關于多線程有關的概念。進程:進程指正在運行的程序。確切的來說,當一個程序進入內存運行,即變成一個進程,進程是處于運行過程中的程序,并且具有一定獨立功能。線程:線程是進程中的一個執(zhí)行單元,負責當前進程中程序的執(zhí)行,一個進程中至少有一個線程。一個進程中是可以有多個線程的,這個應用程序也可以稱之為多線程程序。簡而言之:一個程序運行后至少有一個進程,一個進程中可以包含多個線程什么是多線程呢?即就是一個程序中有多個線程在同時執(zhí)行。通過下圖來區(qū)別單線程程序與多線程程序的不同:l 單線程程序:即,若有多個任務只能依次執(zhí)行。當上一個任務執(zhí)行結束后,下一個任務開始執(zhí)行。如,去網吧上網,網吧只能讓一個人上網,當這個人下機后,下一個人才能上網。l 多線程程序:即,若有多個任務可以同時執(zhí)行。如,去網吧上網,網吧能夠讓多個人同時上網。1.2 程序運行原理l 分時調度所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間。l 搶占式調度優(yōu)先讓優(yōu)先級高的線程使用 CPU,如果線程的優(yōu)先級相同,那么會隨機選擇一個(線程隨機性),Java使用的為搶占式調度。1.2.1 搶占式調度詳解大部分操作系統都支持多進程并發(fā)運行,現在的操作系統幾乎都支持同時運行多個程序。比如:現在我們上課一邊使用編輯器,一邊使用錄屏軟件,同時還開著畫圖板,dos窗口等軟件。此時,這些程序是在同時運行,”感覺這些軟件好像在同一時刻運行著“。實際上,CPU(中央處理器)使用搶占式調度模式在多個線程間進行著高速的切換。對于CPU的一個核而言,某個時刻,只能執(zhí)行一個線程,而 CPU的在多個線程間切換速度相對我們的感覺要快,看上去就是在同一時刻運行。其實,多線程程序并不能提高程序的運行速度,但能夠提高程序運行效率,讓CPU的使用率更高。1.3 主線程回想我們以前學習中寫過的代碼,當我們在dos命令行中輸入java空格類名回車后,啟動JVM,并且加載對應的class文件。虛擬機并會從main方法開始執(zhí)行我們的程序代碼,一直把main方法的代碼執(zhí)行結束。如果在執(zhí)行過程遇到循環(huán)時間比較長的代碼,那么在循環(huán)之后的其他代碼是不會被馬上執(zhí)行的。如下代碼演示:class DemoString name;Demo(String name) = name;void show()for (int i=1;i=10000 ;i+ )System.out.println(name=+name+,i=+i);class ThreadDemo public static void main(String args) Demo d = new Demo(小強); Demo d2 = new Demo(旺財);d.show();d2.show();System.out.println(Hello World!);若在上述代碼中show方法中的循環(huán)執(zhí)行次數很多,這時在d.show();下面的代碼是不會馬上執(zhí)行的,并且在dos窗口會看到不停的輸出name=小強,i=值,這樣的語句。為什么會這樣呢?原因是:jvm啟動后,必然有一個執(zhí)行路徑(線程)從main方法開始的,一直執(zhí)行到main方法結束,這個線程在java中稱之為主線程。當程序的主線程執(zhí)行時,如果遇到了循環(huán)而導致程序在指定位置停留時間過長,則無法馬上執(zhí)行下面的程序,需要等待循環(huán)結束后能夠執(zhí)行。那么,能否實現一個主線程負責執(zhí)行其中一個循環(huán),再由另一個線程負責其他代碼的執(zhí)行,最終實現多部分代碼同時執(zhí)行的效果?能夠實現同時執(zhí)行,通過Java中的多線程技術來解決該問題。1.4 Thread類該如何創(chuàng)建線程呢?通過API中搜索,查到Thread類。通過閱讀Thread類中的描述。Thread是程序中的執(zhí)行線程。Java 虛擬機允許應用程序并發(fā)地運行多個執(zhí)行線程。l 構造方法l 常用方法繼續(xù)閱讀,發(fā)現創(chuàng)建新執(zhí)行線程有兩種方法。l 一種方法是將類聲明為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。創(chuàng)建對象,開啟線程。run方法相當于其他線程的main方法。l 另一種方法是聲明一個實現 Runnable 接口的類。該類然后實現 run 方法。然后創(chuàng)建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。1.5 創(chuàng)建線程方式一繼承Thread類創(chuàng)建線程的步驟:1 定義一個類繼承Thread。2 重寫run方法。3 創(chuàng)建子類對象,就是創(chuàng)建線程對象。4 調用start方法,開啟線程并讓線程執(zhí)行,同時還會告訴jvm去調用run方法。l 測試類public class Demo01 public static void main(String args) /創(chuàng)建自定義線程對象MyThread mt = new MyThread(新的線程!);/開啟新線程mt.start();/在主方法中執(zhí)行for循環(huán)for (int i = 0; i 10; i+) System.out.println(main線程!+i);l 自定義線程類public class MyThread extends Thread /定義指定線程名稱的構造方法public MyThread(String name) /調用父類的String參數的構造方法,指定線程的名稱super(name);/* * 重寫run方法,完成該線程執(zhí)行的邏輯 */Overridepublic void run() for (int i = 0; i 10; i+) System.out.println(getName()+:正在執(zhí)行!+i);思考:線程對象調用 run方法和調用start方法區(qū)別?線程對象調用run方法不開啟線程。僅是對象調用方法。線程對象調用start開啟線程,并讓jvm調用run方法在開啟的線程中執(zhí)行。1.5.1 繼承Thread類原理我們?yōu)槭裁匆^承Thread類,并調用其的start方法才能開啟線程呢?繼承Thread類:因為Thread類用來描述線程,具備線程應該有功能。那為什么不直接創(chuàng)建Thread類的對象呢?如下代碼:Thread t1 = new Thread();t1.start();/這樣做沒有錯,但是該start調用的是Thread類中的run方法,而這個run方法沒有做什么事情,更重要的是這個run方法中并沒有定義我們需要讓線程執(zhí)行的代碼。創(chuàng)建線程的目的是什么?是為了建立程序單獨的執(zhí)行路徑,讓多部分代碼實現同時執(zhí)行。也就是說線程創(chuàng)建并執(zhí)行需要給定線程要執(zhí)行的任務。對于之前所講的主線程,它的任務定義在main函數中。自定義線程需要執(zhí)行的任務都定義在run方法中。Thread類run方法中的任務并不是我們所需要的,只有重寫這個run方法。既然Thread類已經定義了線程任務的編寫位置(run方法),那么只要在編寫位置(run方法)中定義任務代碼即可。所以進行了重寫run方法動作。1.5.2 多線程的內存圖解多線程執(zhí)行時,到底在內存中是如何運行的呢?以上個程序為例,進行圖解說明:多線程執(zhí)行時,在棧內存中,其實每一個執(zhí)行線程都有一片自己所屬的棧內存空間。進行方法的壓棧和彈棧。當執(zhí)行線程的任務結束了,線程自動在棧內存中釋放了。但是當所有的執(zhí)行線程都結束了,那么進程就結束了。1.5.3 獲取線程名稱開啟的線程都會有自己的獨立運行棧內存,那么這些運行的線程的名字是什么呢?該如何獲取呢?既然是線程的名字,按照面向對象的特點,是哪個對象的屬性和誰的功能,那么我們就去找那個對象就可以了。查閱Thread類的API文檔發(fā)現有個方法是獲取當前正在運行的線程對象。還有個方法是獲取當前線程對象的名稱。既然找到了,我們就可以試試。l Thread.currentThread()獲取當前線程對象l Thread.currentThread().getName();獲取當前線程對象的名稱class MyThread extends Thread /繼承ThreadMyThread(String name)super(name);/復寫其中的run方法public void run()for (int i=1;i=20 ;i+ )System.out.println(Thread.currentThread().getName()+,i=+i);class ThreadDemo public static void main(String args) /創(chuàng)建兩個線程任務MyThread d = new MyThread();MyThread d2 = new MyThread();d.run();/沒有開啟新線程, 在主線程調用run方法d2.start();/開啟一個新線程,新線程調用run方法通過結果觀察,原來主線程的名稱:main;自定義的線程:Thread-0,線程多個時,數字順延。如Thread-1.進行多線程編程時,不要忘記了Java程序運行是從主線程開始,main方法就是主線程的線程執(zhí)行內容。1.6 創(chuàng)建線程方式實現Runnable接口創(chuàng)建線程的另一種方法是聲明實現 Runnable 接口的類。該類然后實現 run 方法。然后創(chuàng)建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。為何要實現Runnable接口,Runable是啥玩意呢?繼續(xù)API搜索。查看Runnable接口說明文檔:Runnable接口用來指定每個線程要執(zhí)行的任務。包含了一個 run 的無參數抽象方法,需要由接口實現類重寫該方法。l 接口中的方法l Thread類構造方法創(chuàng)建線程的步驟。1、定義類實現Runnable接口。2、覆蓋接口中的run方法。3、創(chuàng)建Thread類的對象4、將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數。5、調用Thread類的start方法開啟線程。l 代碼演示:public class Demo02 public static void main(String args) /創(chuàng)建線程執(zhí)行目標類對象Runnable runn = new MyRunnable();/將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數Thread thread = new Thread(runn);Thread thread2 = new Thread(runn);/開啟線程thread.start();thread2.start();for (int i = 0; i 10; i+) System.out.println(main線程:正在執(zhí)行!+i);l 自定義線程執(zhí)行任務類public class MyRunnable implements Runnable/定義線程要執(zhí)行的run方法邏輯Overridepublic void run() for (int i = 0; i 10; i+) System.out.println(我的線程:正在執(zhí)行!+i);1.6.1 實現Runnable的原理為什么需要定一個類去實現Runnable接口呢?繼承Thread類和實現Runnable接口有啥區(qū)別呢?實現Runnable接口,避免了繼承Thread類的單繼承局限性。覆蓋Runnable接口中的run方法,將線程任務代碼定義到run方法中。創(chuàng)建Thread類的對象,只有創(chuàng)建Thread類的對象才可以創(chuàng)建線程。線程任務已被封裝到Runnable接口的run方法中,而這個run方法所屬于Runnable接口的子類對象,所以將這個子類對象作為參數傳遞給Thread的構造函數,這樣,線程對象創(chuàng)建時就可以明確要運行的線程的任務。1.6.2 實現Runnable的好處第二種方式實現Runnable接口避免了單繼承的局限性,所以較為常用。實現Runnable接口的方式,更加的符合面向對象,線程分為兩部分,一部分線程對象,一部分線程任務。繼承Thread類,線程對象和線程任務耦合在一起。一旦創(chuàng)建Thread類的子類對象,既是線程對象,有又有線程任務。實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。1.7 線程的匿名內部類使用使用線程的內匿名內部類方式,可以方便的實現每個線程執(zhí)行不同的線程任務操作。l 方式1:創(chuàng)建線程對象時,直接重寫Thread類中的run方法new Thread() public void run() for (int x = 0; x 40; x+) System.out.println(Thread.currentThread().getName()+ .X. + x);.start();l 方式2:使用匿名內部類的方式實現Runnable接口,重新Runnable接口中的run方法Runnable r = new Runnable() public void run() for (int x = 0; x 40; x+) System.out.println(Thread.currentThread().getName()+ .Y. + x);new Thread(r).start();第2章 線程池2.1 線程池概念線程池,其實就是一個容納多個線程的容器,其中的線程可以反復使用,省去了頻繁創(chuàng)建線程對象的操作,無需反復創(chuàng)建線程而消耗過多資源。我們詳細的解釋一下為什么要使用線程池?在java中,如果每個請求到達就創(chuàng)建一個新線程,開銷是相當大的。在實際使用中,創(chuàng)建和銷毀線程花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的用戶請求的時間和資源要多的多。除了創(chuàng)建和銷毀線程的開銷之外,活動的線程也需要消耗系統資源。如果在一個jvm里創(chuàng)建太多的線程,可能會使系統由于過度消耗內存或“切換過度”而導致系統資源不足。為了防止資源不足,需要采取一些辦法來限制任何給定時刻處理的請求數目,盡可能減少創(chuàng)建和銷毀線程的次數,特別是一些資源耗費比較大的線程的創(chuàng)建和銷毀,盡量利用已有對象來進行服務。線程池主要用來解決線程生命周期開銷問題和資源不足問題。通過對多個任務重復使用線程,線程創(chuàng)建的開銷就被分攤到了多個任務上了,而且由于在請求到達時線程已經存在,所以消除了線程創(chuàng)建所帶來的延遲。這樣,就可以立即為請求服務,使用應用程序響應更快。另外,通過適當的調整線程中的線程數目可以防止出現資源不足的情況。2.2 使用線程池方式-Runnable接口通常,線程池都是通過線程池工廠創(chuàng)建,再調用線程池中的方法獲取線程,再通過線程去執(zhí)行任務方法。l Executors:線程池創(chuàng)建工廠類n public static ExecutorService newFixedThreadPool(int nThreads):返回線程池對象l ExecutorService:線程池類n Future submit(Runnabletask):獲取線程池中的某一個線程對象,并執(zhí)行l(wèi) Future接口:用來記錄線程任務執(zhí)行完畢后產生的結果。線程池創(chuàng)建與使用l 使用線程池中線程對象的步驟:n 創(chuàng)建線程池對象n 創(chuàng)建Runnable接口子類對象n 提交Runnable接口子類對象n 關閉線程池代碼演示:public class ThreadPoolDemo public static void main(String args) /創(chuàng)建線程池對象ExecutorService service = Executors.newFixedThreadPool(2);/包含2個線程對象/創(chuàng)建Runnable實例對象MyRunnable r = new MyRunnable();/自己創(chuàng)建線程對象的方式/Thread t = new Thread(r);/t.start(); - 調用MyRunnable中的run()/從線程池中獲取線程對象,然后調用MyRunnable中的run()service.submit(r);/再獲取個線程對象,調用MyRunnable中的run()service.submit(r);service.submit(r);/注意:submit方法調用結束后,程序并不終止,是因為線程池控制了線程的關閉。將使用完的線程又歸還到了線程池中/關閉線程池/service.shutdown();l Runnable接口實現類public class MyRunnable implements Runnable Overridepublic void run() System.out.println(我要一個教練);try Thread.sleep(2000); catch (InterruptedException e) e.printStackTrace();System.out.println(教練來了: +Thread.currentThread().getName();System.out.println(教我游泳,交完后,教練回到了游泳池);2.3 使用線程池方式Callable接口l Callable接口:與Runnable接口功能相似,用來指定線程的任務。其中的call()方法,用來返回線程任務執(zhí)行完畢后的結果,call方法可拋出異常。l ExecutorService:線程池類n Future submit(Callabletask):獲取線程池中的某一個線程對象,并執(zhí)行線程中的call()方法l Future接口:用來記錄線程任務執(zhí)行完畢后產生的結果。線程池創(chuàng)建與使用l 使用線程池中線程對象的步驟:n 創(chuàng)建線程池對象n 創(chuàng)建Callable接口子類對象n 提交Callable接口子類對象n 關閉線程池代碼演示:public class ThreadPoolDemo public static void main(String args) /創(chuàng)建線程池對象ExecutorService service = Executors.newFixedThreadPool(2);/包含2個線程對象/創(chuàng)建Callable對象MyCallable c = new MyCallable();/從線程池中獲取線程對象,然后調用MyRunnable中的run()service.submit(c);/再獲取個教練service.submit(c);service.submit(c);/注意:submit方法調用結束后,程序并不終止,是因為線程池控制了線程的關閉。將使用完的線程又歸還到了線程池中/關閉線程池/service.shutdown();l Callable接口實現類,call方法可拋出異常、返回線程任務執(zhí)行完畢后的結果public class MyCallable implements Callable Overridepublic Object call() throws Exception System.out.println(我要一個教練:call);Thread.sleep(2000);System.out.println(教練來了: +Thread.currentThread().getName();System.out.println(教我游泳,交完后,教練回到了游泳池);return null;2.4 線程池練習:返回兩個數相加的結果要求:通過線程池中的線程對象,使用Callable接口完成兩個數求和操作l Future接口:用來記錄線程任務執(zhí)行完畢后產生的結果。線程池創(chuàng)建與使用n V get() 獲取Future對象中封裝的數據結果代碼演示:public class ThreadPoolDemo public static void main(String args) throws InterruptedException, ExecutionException /創(chuàng)建線程池對象ExecutorService threadPool = Executors.newFixedThreadPool(2);/創(chuàng)建一個Cal

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論