日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java线程的创建线程_多线程(Thread、线程创建、线程池)

發布時間:2023/12/3 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java线程的创建线程_多线程(Thread、线程创建、线程池) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第1章?多線程

1.1?多線程介紹

學習多線程之前,我們先要了解幾個關于多線程有關的概念。

進程:進程指正在運行的程序。確切的來說,當一個程序進入內存運行,即變成一個進程,進程是處于運行過程中的程序,并且具有一定獨立功能。

線程:線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是可以有多個線程的,這個應用程序也可以稱之為多線程程序。

簡而言之:一個程序運行后至少有一個進程,一個進程中可以包含多個線程

什么是多線程呢?即就是一個程序中有多個線程在同時執行。

通過下圖來區別單線程程序與多線程程序的不同:

l?單線程程序:即,若有多個任務只能依次執行。當上一個任務執行結束后,下一個任務開始執行。如,去網吧上網,網吧只能讓一個人上網,當這個人下機后,下一個人才能上網。

l?多線程程序:即,若有多個任務可以同時執行。如,去網吧上網,網吧能夠讓多個人同時上網。

1.2?程序運行原理

l?分時調度

所有線程輪流使用 CPU 的使用權,平均分配每個線程占用CPU的時間。

l?搶占式調度

優先讓優先級高的線程使用 CPU,如果線程的優先級相同,那么會隨機選擇一個(線程隨機性),Java使用的為搶占式調度。

1.2.1?搶占式調度詳解

大部分操作系統都支持多進程并發運行,現在的操作系統幾乎都支持同時運行多個程序。比如:現在我們上課一邊使用編輯器,一邊使用錄屏軟件,同時還開著畫圖板,dos窗口等軟件。此時,這些程序是在同時運行,”感覺這些軟件好像在同一時刻運行著“。

實際上,CPU(中央處理器)使用搶占式調度模式在多個線程間進行著高速的切換。對于CPU的一個核而言,某個時刻,只能執行一個線程,而CPU的在多個線程間切換速度相對我們的感覺要快,看上去就是在同一時刻運行。

其實,多線程程序并不能提高程序的運行速度,但能夠提高程序運行效率,讓CPU的使用率更高。

1.3?主線程

回想我們以前學習中寫過的代碼,當我們在dos命令行中輸入java空格類名回車后,啟動JVM,并且加載對應的class文件。虛擬機并會從main方法開始執行我們的程序代碼,一直把main方法的代碼執行結束。如果在執行過程遇到循環時間比較長的代碼,那么在循環之后的其他代碼是不會被馬上執行的。如下代碼演示:

class Demo{

String name;

Demo(String name){

this.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方法中的循環執行次數很多,這時在d.show();下面的代碼是不會馬上執行的,并且在dos窗口會看到不停的輸出name=小強,i=值,這樣的語句。為什么會這樣呢?

原因是:jvm啟動后,必然有一個執行路徑(線程)從main方法開始的,一直執行到main方法結束,這個線程在java中稱之為主線程。當程序的主線程執行時,如果遇到了循環而導致程序在指定位置停留時間過長,則無法馬上執行下面的程序,需要等待循環結束后能夠執行。

那么,能否實現一個主線程負責執行其中一個循環,再由另一個線程負責其他代碼的執行,最終實現多部分代碼同時執行的效果?

能夠實現同時執行,通過Java中的多線程技術來解決該問題。

1.4?Thread類

該如何創建線程呢?通過API中搜索,查到Thread類。通過閱讀Thread類中的描述。Thread是程序中的執行線程。Java 虛擬機允許應用程序并發地運行多個執行線程。

l?構造方法

l?常用方法

繼續閱讀,發現創建新執行線程有兩種方法。

l?一種方法是將類聲明為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。創建對象,開啟線程。run方法相當于其他線程的main方法。

l?另一種方法是聲明一個實現 Runnable 接口的類。該類然后實現 run 方法。然后創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。

1.5?創建線程方式一繼承Thread類

創建線程的步驟:

1 定義一個類繼承Thread。

2 重寫run方法。

3 創建子類對象,就是創建線程對象。

4 調用start方法,開啟線程并讓線程執行,同時還會告訴jvm去調用run方法。

l?測試類

public class Demo01 {

public static void main(String[] args) {

//創建自定義線程對象

MyThread mt = new MyThread("新的線程!");

//開啟新線程

mt.start();

//在主方法中執行for循環

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方法,完成該線程執行的邏輯

*/

@Override

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println(getName()+":正在執行!"+i);

}

}

}

思考:線程對象調用 run方法和調用start方法區別?

線程對象調用run方法不開啟線程。僅是對象調用方法。線程對象調用start開啟線程,并讓jvm調用run方法在開啟的線程中執行。

1.5.1?繼承Thread類原理

我們為什么要繼承Thread類,并調用其的start方法才能開啟線程呢?

繼承Thread類:因為Thread類用來描述線程,具備線程應該有功能。那為什么不直接創建Thread類的對象呢?如下代碼:

Thread t1 = new Thread();

t1.start();//這樣做沒有錯,但是該start調用的是Thread類中的run方法,而這個run方法沒有做什么事情,更重要的是這個run方法中并沒有定義我們需要讓線程執行的代碼。

創建線程的目的是什么?

是為了建立程序單獨的執行路徑,讓多部分代碼實現同時執行。也就是說線程創建并執行需要給定線程要執行的任務。

對于之前所講的主線程,它的任務定義在main函數中。自定義線程需要執行的任務都定義在run方法中。

Thread類run方法中的任務并不是我們所需要的,只有重寫這個run方法。既然Thread類已經定義了線程任務的編寫位置(run方法),那么只要在編寫位置(run方法)中定義任務代碼即可。所以進行了重寫run方法動作。

1.5.2?多線程的內存圖解

多線程執行時,到底在內存中是如何運行的呢?

以上個程序為例,進行圖解說明:

多線程執行時,在棧內存中,其實每一個執行線程都有一片自己所屬的棧內存空間。進行方法的壓棧和彈棧。

當執行線程的任務結束了,線程自動在棧內存中釋放了。但是當所有的執行線程都結束了,那么進程就結束了。

1.5.3?獲取線程名稱

開啟的線程都會有自己的獨立運行棧內存,那么這些運行的線程的名字是什么呢?該如何獲取呢?既然是線程的名字,按照面向對象的特點,是哪個對象的屬性和誰的功能,那么我們就去找那個對象就可以了。查閱Thread類的API文檔發現有個方法是獲取當前正在運行的線程對象。還有個方法是獲取當前線程對象的名稱。既然找到了,我們就可以試試。

l?Thread.currentThread()獲取當前線程對象

l?Thread.currentThread().getName();獲取當前線程對象的名稱

class MyThread extends Thread { ?//繼承Thread

MyThread(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) {

//創建兩個線程任務

MyThread d = new MyThread();

MyThread d2 = new MyThread();

d.run();//沒有開啟新線程,在主線程調用run方法

d2.start();//開啟一個新線程,新線程調用run方法

}

}

通過結果觀察,原來主線程的名稱:main;自定義的線程:Thread-0,線程多個時,數字順延。如Thread-1......

進行多線程編程時,不要忘記了Java程序運行是從主線程開始,main方法就是主線程的線程執行內容。

1.6?創建線程方式—實現Runnable接口

創建線程的另一種方法是聲明實現 Runnable 接口的類。該類然后實現run方法。然后創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。

為何要實現Runnable接口,Runable是啥玩意呢?繼續API搜索。

查看Runnable接口說明文檔:Runnable接口用來指定每個線程要執行的任務。包含了一個run的無參數抽象方法,需要由接口實現類重寫該方法。

l?接口中的方法

l?Thread類構造方法

創建線程的步驟。

1、定義類實現Runnable接口。

2、覆蓋接口中的run方法。。

3、創建Thread類的對象

4、將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數。

5、調用Thread類的start方法開啟線程。

l?代碼演示:

public class Demo02 {

public static void main(String[] args) {

//創建線程執行目標類對象

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線程:正在執行!"+i);

}

}

}

l?自定義線程執行任務類

public class MyRunnable implements Runnable{

//定義線程要執行的run方法邏輯

@Override

public void run() {

for (int i = 0; i < 10; i++) {

System.out.println("我的線程:正在執行!"+i);

}

}

}

1.6.1?實現Runnable的原理

為什么需要定一個類去實現Runnable接口呢?繼承Thread類和實現Runnable接口有啥區別呢?

實現Runnable接口,避免了繼承Thread類的單繼承局限性。覆蓋Runnable接口中的run方法,將線程任務代碼定義到run方法中。

創建Thread類的對象,只有創建Thread類的對象才可以創建線程。線程任務已被封裝到Runnable接口的run方法中,而這個run方法所屬于Runnable接口的子類對象,所以將這個子類對象作為參數傳遞給Thread的構造函數,這樣,線程對象創建時就可以明確要運行的線程的任務。

1.6.2?實現Runnable的好處

第二種方式實現Runnable接口避免了單繼承的局限性,所以較為常用。實現Runnable接口的方式,更加的符合面向對象,線程分為兩部分,一部分線程對象,一部分線程任務。繼承Thread類,線程對象和線程任務耦合在一起。一旦創建Thread類的子類對象,既是線程對象,有又有線程任務。實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。

1.7?線程的匿名內部類使用

使用線程的內匿名內部類方式,可以方便的實現每個線程執行不同的線程任務操作。

l?方式1:創建線程對象時,直接重寫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?線程池概念

線程池,其實就是一個容納多個線程的容器,其中的線程可以反復使用,省去了頻繁創建線程對象的操作,無需反復創建線程而消耗過多資源。

我們詳細的解釋一下為什么要使用線程池?

在java中,如果每個請求到達就創建一個新線程,開銷是相當大的。在實際使用中,創建和銷毀線程花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的用戶請求的時間和資源要多的多。除了創建和銷毀線程的開銷之外,活動的線程也需要消耗系統資源。如果在一個jvm里創建太多的線程,可能會使系統由于過度消耗內存或“切換過度”而導致系統資源不足。為了防止資源不足,需要采取一些辦法來限制任何給定時刻處理的請求數目,盡可能減少創建和銷毀線程的次數,特別是一些資源耗費比較大的線程的創建和銷毀,盡量利用已有對象來進行服務。

線程池主要用來解決線程生命周期開銷問題和資源不足問題。通過對多個任務重復使用線程,線程創建的開銷就被分攤到了多個任務上了,而且由于在請求到達時線程已經存在,所以消除了線程創建所帶來的延遲。這樣,就可以立即為請求服務,使用應用程序響應更快。另外,通過適當的調整線程中的線程數目可以防止出現資源不足的情況。

2.2?使用線程池方式--Runnable接口

通常,線程池都是通過線程池工廠創建,再調用線程池中的方法獲取線程,再通過線程去執行任務方法。

l?Executors:線程池創建工廠類

l?public static ExecutorService newFixedThreadPool(int nThreads):返回線程池對象

l?ExecutorService:線程池類

l?Future> submit(Runnable?task):獲取線程池中的某一個線程對象,并執行

l?Future接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用

l?使用線程池中線程對象的步驟:

l?創建線程池對象

l?創建Runnable接口子類對象

l?提交Runnable接口子類對象

l?關閉線程池

代碼演示:

public class ThreadPoolDemo {

public static void main(String[] args) {

//創建線程池對象

ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象

//創建Runnable實例對象

MyRunnable r = new MyRunnable();

//自己創建線程對象的方式

//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 {

@Override

public 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()方法,用來返回線程任務執行完畢后的結果,call方法可拋出異常。

l?ExecutorService:線程池類

l? Future submit(Callable?task):獲取線程池中的某一個線程對象,并執行線程中的call()方法

l?Future接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用

l?使用線程池中線程對象的步驟:

l?創建線程池對象

l?創建Callable接口子類對象

l?提交Callable接口子類對象

l?關閉線程池

代碼演示:

public class ThreadPoolDemo {

public static void main(String[] args) {

//創建線程池對象

ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象

//創建Callable對象

MyCallable c = new MyCallable();

//從線程池中獲取線程對象,然后調用MyRunnable中的run()

service.submit(c);

//再獲取個教練

service.submit(c);

service.submit(c);

//注意:submit方法調用結束后,程序并不終止,是因為線程池控制了線程的關閉。將使用完的線程又歸還到了線程池中

//關閉線程池

//service.shutdown();

}

}

l?Callable接口實現類,call方法可拋出異常、返回線程任務執行完畢后的結果

public class MyCallable implements Callable {

@Override

public 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接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用

l?V get() 獲取Future對象中封裝的數據結果

代碼演示:

public class ThreadPoolDemo {

public static void main(String[] args) throws InterruptedException, ExecutionException {

//創建線程池對象

ExecutorService threadPool = Executors.newFixedThreadPool(2);

//創建一個Callable接口子類對象

//MyCallable c = new MyCallable();

MyCallable c = new MyCallable(100, 200);

MyCallable c2 = new MyCallable(10, 20);

//獲取線程池中的線程,調用Callable接口子類對象中的call()方法,完成求和操作

// Future submit(Callable task)

// Future 結果對象

Future result = threadPool.submit(c);

//此Future的get方法所返回的結果類型

Integer sum = result.get();

System.out.println("sum=" + sum);

//再演示

result = threadPool.submit(c2);

sum = result.get();

System.out.println("sum=" + sum);

//關閉線程池(可以不關閉)

}

}

l?Callable接口實現類

public class MyCallable implements Callable {

//成員變量

int x = 5;

int y = 3;

//構造方法

public MyCallable(){

}

public MyCallable(int x, int y){

this.x = x;

this.y = y;

}

@Override

public Integer call() throws Exception {

return x+y;

}

}

第3章?總結

3.1?知識點總結

l?創建線程的方式

l?方式1,繼承Thread線程類

l?步驟

1,?自定義類繼承Thread類

2,?在自定義類中重寫Thread類的run方法

3,?創建自定義類對象(線程對象)

4,?調用start方法,啟動線程,通過JVM,調用線程中的run方法

l?方式2,實現Runnable接口

l?步驟

1,?創建線程任務類 實現Runnable接口

2,?在線程任務類中 重寫接口中的run方法

3,?創建線程任務類對象

4,?創建線程對象,把線程任務類對象作為Thread類構造方法的參數使用

5,?調用start方法,啟動線程,通過JVM,調用線程任務類中的run方法

總結

以上是生活随笔為你收集整理的java线程的创建线程_多线程(Thread、线程创建、线程池)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。