目錄
前言
一.Java的多線程
1.1多線程的認(rèn)識?
1.2Java多線程的創(chuàng)建方式
1.3Java多線程的生命周期
1.4Java多線程的執(zhí)行機(jī)制
二.創(chuàng)建多線程的四種方式
2.1繼承Thread類
?創(chuàng)建線程?
?Thread的構(gòu)造方法和常見屬性
?2.2.實現(xiàn)Runnable接口
?創(chuàng)建線程
?使用lambda表達(dá)式創(chuàng)建
2.3實現(xiàn)Callable接口創(chuàng)建多線程
?線程的創(chuàng)建
?Callable接口的特點
2.4通過線程池創(chuàng)建多線程
?創(chuàng)建線程
??個人主頁:tq02的博客_CSDN博客-C語言,Java,Java數(shù)據(jù)結(jié)構(gòu)領(lǐng)域博主
?? 本文由 tq02 原創(chuàng),首發(fā)于 CSDN??
???本章講解內(nèi)容:多線程的認(rèn)識、創(chuàng)建方式、及其狀態(tài)
?
??學(xué)習(xí)專欄:??C語言?? ? ? ??JavaSE?? ? ??MySQL基礎(chǔ)??
前言
? ? ? ? 在學(xué)習(xí)多線程之前,我們必須了解什么是線程?作用是什么?而線程的知識又與進(jìn)程有關(guān)系,因此我們需要先了解進(jìn)程再去了解線程,這樣才能更好的學(xué)習(xí)到多線程的知識。本文只是多線程的一部分,多線程涉及的知識點很多很多,鎖啊、線程安全啊、CAS等知識,需要耐心學(xué)習(xí)。
進(jìn)程學(xué)習(xí):http://t.csdn.cn/I4uDU
線程學(xué)習(xí):http://t.csdn.cn/AxYac
一.Java的多線程
1.1多線程的認(rèn)識?
? ? ?多線程,從字面上理解,就是從多個單線程一起執(zhí)行多個任務(wù)。在Java 編程中,已經(jīng)給多線程編程提供了內(nèi)置的支持。多線程是多任務(wù)的一種特別的形式,但多線程使用了更小的cpu資源開銷。 多線程能滿足程序員編寫高效率的程序來達(dá)到充分利用 CPU 的目的。
? ? ? ? 線程本身就是操作系統(tǒng)提供的概念,因此操作系統(tǒng)提供了一些API供程序員使用,而在Java中,也存在一些API供人們使用和編譯。在Java標(biāo)準(zhǔn)庫中Thread類就是用于多線程的創(chuàng)建。
注:創(chuàng)建多線程的方式不僅僅只有Thread類
1.2Java多線程的創(chuàng)建方式
Java語言中,目前可以創(chuàng)建多線程的方式有四種方式:
- 繼承Thread類
- 實現(xiàn)Runnable接口
- 使用lambda表達(dá)式(基于Runnable接口搭配內(nèi)部類的優(yōu)化)
- 使用線程池
- 使用FutureTask類和Callable接口
以上的1、2、5方法可以搭配匿名內(nèi)部類使用,而目前最為常用的方式有:實現(xiàn)Runnable接口、調(diào)用多線程池。
1.3Java多線程的生命周期
?????????新建狀態(tài)、可執(zhí)行狀態(tài)、執(zhí)行狀態(tài)以及死亡狀態(tài)是每一個線程都會發(fā)生,而阻塞狀態(tài)則是選擇性發(fā)生,當(dāng)需要阻塞時,使用sleep()、join()方法。
1.4Java多線程的執(zhí)行機(jī)制
? ? ? ? 當(dāng)Java程序運行時,先創(chuàng)建出一個進(jìn)程,該進(jìn)程里至少包含一個線程,主線程,就是負(fù)責(zé)執(zhí)行main方法的線程。然后在mian()方法里創(chuàng)建出其他線程。我們主要學(xué)習(xí)的就是創(chuàng)建和使用線程。
?注:一般情況下,主線程與子線程相互不影響,即子線程結(jié)束,主線程不一定結(jié)束;主線程結(jié)束,子線程不一定結(jié)束;主線程異常,子線程不一定異常;子線程異常,主線程不一定異常。但當(dāng)設(shè)置守護(hù)線程等特殊操作時,主線程與子線程會發(fā)生相互影響。
?
二.創(chuàng)建多線程的四種方式
2.1繼承Thread類
?創(chuàng)建線程?
? ? ? ? 使用Thread類創(chuàng)建線程有2種方式,最基本的實現(xiàn)多線程方式,就是創(chuàng)建一個類繼承Thread類,然后再實例化該類。可實例化多個線程。
1.繼承Thread類創(chuàng)建一個線程
class MyThread extends Thread {
@Override
public void run() {
System.out.println("這里是線程運行的代碼");
}
}
public class Text{
public static void main(String[] args) {
//創(chuàng)建MyThread實例
MyThread t1=new MyThread();
//調(diào)用start方法啟動線程
t1.start();
}
}
注:繼承Thread類需要重寫run()方法,而調(diào)用的是start()方法,而不是run()方法,start()是啟動線程,而run()則是執(zhí)行方法。
2.匿名內(nèi)部類創(chuàng)建 Thread 子類對象
// 使用匿名類創(chuàng)建 Thread 子類對象
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("使用匿名類創(chuàng)建 Thread 子類對象");
}
};
//啟動線程
t1.start();
注:該方法不需要繼承其他類,而是直接實例化Thread類和使用匿名內(nèi)部類。?
?Thread的構(gòu)造方法和常見屬性
Thread構(gòu)造方法:
構(gòu)造方法 | 解釋 |
Thread() | 創(chuàng)建線程 |
Thread(Runnable 對象名) | 使用Runnable對象創(chuàng)建線程 |
Thread(String 線程名) | 創(chuàng)建線程對象,并命名 |
Thread(Runnable 對象名,String 線程名) | 使用Runnable對象創(chuàng)建線程對象,并命名 |
常見屬性:
屬性 | 獲取方法 |
ID | getId() |
名稱 | getName() |
狀態(tài) | getState() |
優(yōu)先級 | getPriority() |
是否有后臺線程 | isDaemon() |
是否存活 | isAlive() |
是否中斷 | isInterrupted() |
ID 是線程的唯一標(biāo)識,不同線程不會重復(fù)
名稱是各種調(diào)試工具用到
其中重要的是前臺和后臺線程:
- 前臺線程,會影響到進(jìn)程結(jié)束,如果前臺進(jìn)程沒有執(zhí)行完畢,進(jìn)程不會結(jié)束
- 后臺線程,也稱守護(hù)線程,當(dāng)主線程結(jié)束時,進(jìn)程結(jié)束,后臺線程無論是否還在執(zhí)行也結(jié)束
?Java多線程當(dāng)中默認(rèn)為前臺線程,也可以通過setDaemon方法設(shè)置,false
存活是指線程是否執(zhí)行結(jié)束。中斷是指讓正在執(zhí)行的線程強行結(jié)束。類似循環(huán)的break;
?2.2.實現(xiàn)Runnable接口
?創(chuàng)建線程
????????Runnable接口,將需要執(zhí)行的線程放入其中,再通過Runnable和Thread配合,就可以進(jìn)行線程的實現(xiàn)。而方法有2種。
第一種:創(chuàng)建Thread類實例,調(diào)用構(gòu)造方法時,將Runnable對象傳參
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("這里是線程運行的代碼");
}
}
//創(chuàng)建Thread類實例
Thread t = new Thread(new MyRunnable());
//調(diào)用start()方法
t.start();
? ? ? ? 實現(xiàn)一個接口Runnable,然后重寫run方法,再將Runnable實例化出的對象,放入Thread的構(gòu)造函數(shù)Thread(Runnable 對象名),就可以實現(xiàn)線程的執(zhí)行。
第二種:創(chuàng)建Thread類實例,搭配使用匿名內(nèi)部類創(chuàng)建Runnable子類對象
// 使用匿名類創(chuàng)建 Runnable 子類對象
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名類創(chuàng)建 Runnable 子類對象");
}
});
????????這種方法更為的簡便,直接在匿名內(nèi)部類當(dāng)中重寫run()方法,在run()方法內(nèi)部寫上需要執(zhí)行的操作。
?使用lambda表達(dá)式創(chuàng)建
? ? ? ? lambda表達(dá)式用于匿名內(nèi)部類和接口的情況,而創(chuàng)建Thread類時,搭配匿名內(nèi)部類創(chuàng)建了Runnable子類對象,因此可以使用lambda表達(dá)式的方法簡化操作。
lambda表達(dá)式的學(xué)習(xí):http://t.csdn.cn/VxRLy
代碼示例:
// 使用 lambda 表達(dá)式創(chuàng)建 Runnable 子類對象
Thread t4 = new Thread(() -> {
System.out.println("使用匿名類創(chuàng)建 Thread 子類對象");
});
如果看不懂lambda表達(dá)式,你就記住這是一種創(chuàng)建線程的方法,不去理解。
注:相比前幾種創(chuàng)建方式,使用lambda表達(dá)式創(chuàng)建更為方便簡單。
2.3實現(xiàn)Callable接口創(chuàng)建多線程
?線程的創(chuàng)建
?Callable 是一個 interface . 使用時需要創(chuàng)建utureTask類的對象,相當(dāng)于把線程封裝了一個 "返回值". 方便程序猿借助多線程的方式計算結(jié)果.該方法是基于jdk5.0之后新增的方法,
方法步驟:
- 創(chuàng)建一個實現(xiàn)Callable接口的類。
- 在這個實現(xiàn)類中實現(xiàn)Callable接口的call()方法,并創(chuàng)建這個類的對象。? ? ? ?
- 將這個Callable接口實現(xiàn)類的對象作為參數(shù)傳遞到FutureTask類的構(gòu)造器中,創(chuàng)建FutureTask類的對象。? ? ? ?
- 將這個FutureTask類的對象作為參數(shù)傳遞到Thread類的構(gòu)造器中,創(chuàng)建Thread類的對象,并調(diào)用這個對象的start()方法。
- 在主線程中調(diào)用 futureTask.get() 能夠阻塞等待新線程計算完畢. 并獲取到 FutureTask 中的結(jié)
果。
代碼示例:
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 1000; i++) {
sum += i;
}
return sum;
}
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread t = new Thread(futureTask);
t.start();
int result = futureTask.get();
System.out.println(result);
?Callable接口的特點
- Callable 和 Runnable 相對, 都是描述一個 "任務(wù)". Callable 描述的是帶有返回值的任務(wù),Runnable 描述的是不帶返回值的任務(wù).
- Callable 通常需要搭配 FutureTask 來使用. FutureTask 用來保存 Callable 的返回結(jié)果. 因為
- Callable 往往是在另一個線程中執(zhí)行的, 啥時候執(zhí)行完并不確定.
- FutureTask 就可以負(fù)責(zé)這個等待結(jié)果出來的工作.
2.4通過線程池創(chuàng)建多線程
????????雖然創(chuàng)建銷毀線程比創(chuàng)建銷毀進(jìn)程更輕量, 但是在頻繁創(chuàng)建銷毀線程的時候還是會比較低效.
線程池就是為了解決這個問題. 如果某個線程不再使用了, 并不是真正把線程釋放, 而是放到一個 "池子"中, 下次如果需要用到線程就直接從池子中取, 不必通過系統(tǒng)來創(chuàng)建了.
?創(chuàng)建線程
1、線程池的使用,需要使用以下類:?
- Executors 是一個工廠類, 能夠創(chuàng)建出幾種不同風(fēng)格的線程池.
- ExecutorService 表示一個線程池實例.
- ExecutorService 的 submit 方法能夠向線程池中提交若干個任務(wù).
2、Executors 創(chuàng)建的幾種風(fēng)格線程池:
實例化一個線程池格式:ExecutorService pool = Executors.方法();
創(chuàng)建線程池種類 | 解釋 |
?Executors.newFixedThreadPool(int? Num threads)? | 創(chuàng)建固定線程數(shù)的線程池 |
?Executors.newSingleThreadExecutor() | 創(chuàng)建只包含單個線程的線程池. |
?Executors.newCachedThreadPool() | 創(chuàng)建線程數(shù)目動態(tài)增長的線程池. |
?Executors.newScheduledThreadPool(int corePoolSize)? | 設(shè)定延遲時間后執(zhí)行命令,或者定期執(zhí)行命令. 是 進(jìn)階版的 Timer. |
注:Executors 本質(zhì)上是 ThreadPoolExecutor 類的封裝
代碼示例:
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
//線程池使用結(jié)束之后需要使用shutdown()關(guān)閉
pool.shutdown();
三.線程的狀態(tài)
????????線程的狀態(tài)是一個枚舉類型 Thread.State,而線程的狀態(tài)一共有6種,6種狀態(tài)定義在Thread類的State枚舉中。
- 初始狀態(tài)(NEW):線程對象被創(chuàng)建但尚未調(diào)用start()方法。
- 就緒狀態(tài)(RUNNABLE):線程處于可運行線程池中,等待被線程調(diào)度選中獲取CPU的使用權(quán)。就緒狀態(tài)分為兩種情況,一是線程對象創(chuàng)建后,其他線程調(diào)用了該對象的start()方法,此時線程處于某個線程拿到對象鎖、當(dāng)前線程時間片用完調(diào)用yield()方法、鎖池里的線程拿到對象鎖后,這些線程也將進(jìn)入就緒狀態(tài)。
- 運行狀態(tài)(RUNNABLE之RUNNING):線程正在被執(zhí)行,具體來說就是線程獲得了CPU的使用權(quán)。
- 阻塞狀態(tài)(BLOCKED):線程阻塞于鎖,即線程在等待獲得對象鎖。
- 等待狀態(tài)(WAITING):線程需要等待其他線程做出一些特定動作,比如等待其他線程的通知或中斷。
- 超時等待狀態(tài)(TIMED_WAITING):與等待狀態(tài)不同的是,超過指定時間后會自動返回。
- 終止?fàn)顟B(tài)(TERMINATED):線程的run()方法執(zhí)行完成,或者主線程的main()方法執(zhí)行完成,線程終止。終止?fàn)顟B(tài)的線程不能再被復(fù)生。如果在終止?fàn)顟B(tài)的線程上調(diào)用start()方法,會拋出java.lang.IllegalThreadStateException異常。
注:之前的 isAlive() 方法,可以認(rèn)為是處于不是 NEW 和 TERMINATED 的狀態(tài)都是活著
的
?
3.1狀態(tài)的抽象說明?
狀態(tài)圖:?
??文章來源:http://www.zghlxwxcb.cn/news/detail-625013.html
- 初始狀態(tài)(NEW):剛剛拿到銀行卡
- 就緒狀態(tài)(RUNNABLE):排隊等待中
- 運行狀態(tài)(RUNNABLE之RUNNING):開始與柜臺人員進(jìn)行交流取錢
- 阻塞狀態(tài)(BLOCKED):阻塞了,相當(dāng)于有人提前預(yù)約了取錢,你只能等待他結(jié)束
- 等待狀態(tài)(WAITING):使用了wait()等待,例如柜臺人員有一點事情,讓你等到他回來
- 超時等待狀態(tài)(TIMED_WAITING):使用了sleep(),柜臺人員告訴了你等他多久;使用join()方法,就是你和你朋友同時在取錢,取錢到一半你說先停下來,我朋友那邊取完了你再取。
????????多線程的概念、創(chuàng)建、狀態(tài)的講解本章已經(jīng)結(jié)束了,而后期還有很多線程操作、線程安全、鎖等知識,因此多線程是一個極為復(fù)雜并且重要的知識。文章來源地址http://www.zghlxwxcb.cn/news/detail-625013.html
到了這里,關(guān)于Java多線程(1)---多線程認(rèn)識、四種創(chuàng)建方式以及線程狀態(tài)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!