Java中Thread類(lèi)的使用和它的屬性
在java中可以進(jìn)行多線(xiàn)程編程,在java標(biāo)準(zhǔn)庫(kù)中提供了一個(gè)Thread類(lèi),來(lái)表示線(xiàn)程操作。Thread類(lèi)可以視為java標(biāo)準(zhǔn)庫(kù)提供的一組解決多線(xiàn)程編程的一組API.
創(chuàng)建好的Thread實(shí)例,和操作系統(tǒng)中的線(xiàn)程是一一對(duì)應(yīng)的。操作系統(tǒng)提供了關(guān)于線(xiàn)程的API(C語(yǔ)言風(fēng)格),java在對(duì)其進(jìn)行封裝就成Thread類(lèi)。
創(chuàng)建線(xiàn)程
方法一:繼承Thread類(lèi)
class MyThread extends Thread{
@Override
public void run() {
//此時(shí)只是定義處理一個(gè)線(xiàn)程類(lèi),在系統(tǒng)中總是還沒(méi)有創(chuàng)建出 新的線(xiàn)程。
System.out.println("hello thread");
}
}
public class TestDemo11 {
public static void main(String[] args) {
//創(chuàng)建線(xiàn)程
Thread t = new MyThread();
//啟動(dòng)線(xiàn)程,在系統(tǒng)中創(chuàng)建出了新的線(xiàn)程
t.start();
}
}

線(xiàn)程之間是并發(fā)執(zhí)行的
class MyThread3 extends Thread{
@Override
public void run() { //定義一個(gè)線(xiàn)程類(lèi)
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestDemo13 {
public static void main(String[] args) {
Thread t = new MyThread3();
t.start();//啟動(dòng)t線(xiàn)程
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


我們可以看到1s當(dāng)執(zhí)行一個(gè)線(xiàn)程中的代碼之后 進(jìn)入阻塞狀態(tài),那么下一秒要喚醒那個(gè)線(xiàn)程呢?
我們可以看到執(zhí)行出來(lái)的兩個(gè)線(xiàn)程中打印出來(lái)的日志的順序是不確定的。每一輪,1s之后,到底是喚醒thread線(xiàn)程還是喚醒main線(xiàn)程,這是不確定的。(搶占式執(zhí)行),對(duì)于操作系統(tǒng)來(lái)說(shuō),從宏觀上對(duì)線(xiàn)程進(jìn)行調(diào)度的順序是隨機(jī)的
此處在說(shuō)明一下Thread.sleep()方法,sleep()這個(gè)方法到了ms級(jí)別沒(méi)有那么精確。當(dāng)調(diào)用這個(gè)方法之后,把線(xiàn)程強(qiáng)制處于中阻塞(睡眠狀態(tài)),但是當(dāng)阻塞時(shí)間結(jié)束之后,并不是立即在cup上繼續(xù)執(zhí)行該線(xiàn)程,如果Thread.sleep(1000),當(dāng)通過(guò)1s后,阻塞時(shí)間結(jié)束,但是在1001ms,線(xiàn)程也許不會(huì)立即執(zhí)行。也許操作系統(tǒng)中的cup在忙別的線(xiàn)程?;蛟S該線(xiàn)程在1006ms才執(zhí)行。
方法二:實(shí)現(xiàn)Runnable接口中的run()方法
//Runnable其實(shí)就是描述一個(gè)任務(wù)
//創(chuàng)建一個(gè)類(lèi),實(shí)現(xiàn)Runnable接口,重寫(xiě)Runnable類(lèi)中的run方法,在run()方法中,描述的是該線(xiàn)程要指向的哪些任務(wù)。
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("hello thread");
}
}
public class TestDemo12 {
public static void main(String[] args) {
//創(chuàng)建線(xiàn)程,把創(chuàng)建好的Runnable實(shí)例傳給Thread實(shí)例
Thread t = new Thread(new MyThread2());
t.start();
}
}
方法三:利用內(nèi)部類(lèi)
方法三其實(shí)是方法一個(gè)的翻版,就是把上面的兩種代碼,改成了匿名內(nèi)部類(lèi)。
public class TestDemo14 {
public static void main(String[] args) {
Thread t1 = new MyThread(){
@Override
public void run() {
System.out.println("hello thread1");
}
};
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello thread2");
}
});
t1.start();
t2.start();
}
}

方法四:使用lambmd表達(dá)式
public class TestDemo15 {
public static void main(String[] args) {
//其實(shí)就是使用lambad表達(dá)式代替Runnable
Thread t1 = new Thread(()->{
//()表示無(wú)參的run()方法
System.out.println("hello thread1");
});
}
}
使用線(xiàn)程的好處
為了更方便的體現(xiàn)出多線(xiàn)程的好處,在這里我們從0開(kāi)始自增1,一直自增到10_0000_0000,使用串行方法,和并行方法,并且獲取他們執(zhí)行代碼的時(shí)間,進(jìn)行比較。
public class TestDemo16 {
public static void func1() throws InterruptedException {
long big = System.currentTimeMillis();
//串行執(zhí)行
Thread t = new Thread(()->{
long a = 0;
for(long i = 0;i<10_0000_0000;i++){
a++;
}
});
t.start();
t.join();
long end = System.currentTimeMillis();
System.out.println("串行消耗時(shí)間:" + (end - big) + "ms");
}
public static void func2() throws InterruptedException {
long big = System.currentTimeMillis();
Thread t1 = new Thread(()->{
long b = 0;
for(long i = 0;i< 10_0000_0000 / 2;i++){
b++;
}
});
t1.start();
Thread t2 = new Thread(()->{
long c = 0;
for(long i = 0;i<10_0000_0000/ 2;i++){
c++;
}
});
t2.start();
t1.join();
t2.join();
long end = System.currentTimeMillis();
System.out.println("并行執(zhí)行消耗時(shí)間:" + (end - big) + "ms");
}
public static void main(String[] args) throws InterruptedException {
func1();//串行執(zhí)行
func2();//并行執(zhí)行
}
}

我們可以很明顯的看出串行時(shí)間要比并行時(shí)間長(zhǎng)的多,串行時(shí)間幾乎是并行時(shí)間的2倍。

Thread類(lèi)的其他屬性和方法
Thread的常見(jiàn)構(gòu)造方法
| 屬性 | 獲取方法 |
|---|---|
| ID | getId() |
| 名稱(chēng) | getName() |
| 狀態(tài) | getState() |
| 優(yōu)先級(jí) | getPriority() |
| 是否后臺(tái)線(xiàn)程 | isDaemon() |
| 線(xiàn)程是否存活 | isAlive() |
| 線(xiàn)程是否被中斷 | isinterrupted() |
- ID是線(xiàn)程唯一的標(biāo)識(shí),不同的線(xiàn)程之間不會(huì)重復(fù)
- 名稱(chēng)是各種調(diào)試工具用到的
- 狀態(tài)標(biāo)識(shí)當(dāng)前線(xiàn)程的一種情況
- 優(yōu)先級(jí)高的線(xiàn)程,理論上更容易被執(zhí)行到
- 是否存活簡(jiǎn)單理解為run()方法是否執(zhí)行結(jié)束
給一個(gè)線(xiàn)程起名字
Thread(String name) 這個(gè)東西是給thread對(duì)象起一個(gè)名字,具體起什么名字和線(xiàn)程的執(zhí)行效率無(wú)關(guān),起名字主要依靠于程序員,方便程序員在后期進(jìn)行調(diào)試。
public class TestDemo17 {
public static void main(String[] args) {
//給線(xiàn)程器名字
Thread t1 = new Thread(()->{
while(true) {
System.out.println("hello thread1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"thread1");
Thread t2 = new Thread(()->{
while(true) {
System.out.println("hello thread2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"thread2");
t1.start();
t2.start();
}
}
那么怎樣才能看到,我們定義好的線(xiàn)程名字呢?
注意:當(dāng)我們要查看線(xiàn)程名字的時(shí)候,程序必須要正在執(zhí)行,否則我們查找不到對(duì)應(yīng)的線(xiàn)程名字。

判斷一個(gè)線(xiàn)程是否存活
簡(jiǎn)單的說(shuō)就是操作系統(tǒng)中我們創(chuàng)建出來(lái)的線(xiàn)程是否還存在
Thread t 的生命周期和操作系統(tǒng)中對(duì)應(yīng)的線(xiàn)程的生命周期并不是完全一致的。
我們?cè)诙x一個(gè)線(xiàn)程類(lèi)后,在調(diào)用t.start()方法之前,操作系統(tǒng)中是沒(méi)有我們創(chuàng)建出來(lái)的線(xiàn)程的。在線(xiàn)程類(lèi)中的run()方法執(zhí)行完之后,我們?cè)诓僮飨到y(tǒng)中創(chuàng)建出來(lái)的線(xiàn)程就被銷(xiāo)毀了!但是線(xiàn)程t對(duì)象還存在。
- 總而言之,在調(diào)用t.start()方法之前,在執(zhí)行run()方法之后,此時(shí)操作系統(tǒng)中是沒(méi)有我們創(chuàng)建出來(lái)的線(xiàn)程的。
- 在調(diào)用t.start()方法之后,在指向run()方法之前,此時(shí)操作系統(tǒng)中存在我們創(chuàng)建出來(lái)的線(xiàn)程 判斷該線(xiàn)程是由是后臺(tái)線(xiàn)程
如果一個(gè)線(xiàn)程是后臺(tái)線(xiàn)程,那么這個(gè)線(xiàn)程就不會(huì)進(jìn)行進(jìn)程退出
如果一個(gè)線(xiàn)程是前臺(tái)線(xiàn)程,那么這個(gè)這個(gè)線(xiàn)程就會(huì)影響到進(jìn)程的退出。我們以上的代碼在創(chuàng)建線(xiàn)程,那些線(xiàn)程都是前臺(tái)線(xiàn)程,假如現(xiàn)在有前臺(tái)線(xiàn)程t1,t2, 現(xiàn)在即使main線(xiàn)程執(zhí)行結(jié)束,但是此時(shí)還不可以退出線(xiàn)程,必須要將t1,t2線(xiàn)程執(zhí)行結(jié)束之后,整個(gè)線(xiàn)程才會(huì)結(jié)束。
假如現(xiàn)在有兩個(gè)線(xiàn)程t1,t2,它們都是后臺(tái)線(xiàn)程,那么如果現(xiàn)在main線(xiàn)程執(zhí)行結(jié)束,整個(gè)進(jìn)程就執(zhí)行結(jié)束,此時(shí)我們會(huì)強(qiáng)制停止t1,t2線(xiàn)程。
public class TestDemo18 {
public static void main(String[] args) {
Thread t = new Thread(()->{
System.out.println("hello thread");
});
t.start();
System.out.println(t.isDaemon());
}
}
//因?yàn)槲覀儎?chuàng)建的是一個(gè)前臺(tái)線(xiàn)程,所以返回false
Thread的其他常見(jiàn)屬性
創(chuàng)建線(xiàn)程
創(chuàng)建線(xiàn)程:定義出一個(gè)線(xiàn)程類(lèi),然后啟動(dòng)線(xiàn)程t.start(),其中start()方法決定系統(tǒng)是不是真的創(chuàng)建出線(xiàn)程。

線(xiàn)程的中斷
中斷線(xiàn)程簡(jiǎn)單的可以理解成為就是讓該線(xiàn)程中的run()方法執(zhí)行結(jié)束。還有一個(gè)特殊的就是main線(xiàn)程,如果想要中斷main線(xiàn)程,那么就需要把main線(xiàn)程執(zhí)行完。
中斷線(xiàn)程方法一:設(shè)置一個(gè)標(biāo)志位
public class TestDemo21 {
public static void main(String[] args) {
Thread t = new Thread(()->{
while(!Thread.currentThread().isInterrupted()) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//啟動(dòng)t線(xiàn)程
t.start();
//在main線(xiàn)程中中斷t線(xiàn)程
//5s之后中斷t線(xiàn)程
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
}
運(yùn)行結(jié)果:當(dāng)t線(xiàn)程中的sout語(yǔ)句被執(zhí)行5次之后,線(xiàn)程停止。
上面的這種寫(xiě)法不夠嚴(yán)謹(jǐn),只適用于該場(chǎng)合,如果化作是別的代碼場(chǎng)合的話(huà),有可能不會(huì)終止線(xiàn)程。
這里用一種較好的方法,使用Thread類(lèi)中自帶的檢查線(xiàn)程是否斷開(kāi)。
Thread.interrputed() 這是一個(gè)靜態(tài)方法 Thread.currentThread().isinterrupted() 其中Thread.cerrentThread()可以獲得線(xiàn)程的引用。
t.interrupted()用于中斷線(xiàn)程
public class TestDemo21 {
public static void main(String[] args) {
Thread t = new Thread(()->{
while(!Thread.currentThread().isInterrupted()) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//啟動(dòng)t線(xiàn)程
t.start();
//在main線(xiàn)程中中斷t線(xiàn)程
//5s之后中斷t線(xiàn)程
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
}


在我們上邊中斷線(xiàn)程,判斷標(biāo)志位的時(shí)候,我們使用的是第三種方法。設(shè)置標(biāo)志位的時(shí)候使用的是第一種方法。
在我們的日常開(kāi)發(fā)中,經(jīng)常會(huì)用到Thread.currentThread().isInterrupted()來(lái)判斷標(biāo)志位,判斷該線(xiàn)程是否被中斷。
還有一種方法判斷標(biāo)志位,但是它是一個(gè)靜態(tài)方法,只能判斷一個(gè)類(lèi)中只有一個(gè)線(xiàn)程的情況下,這個(gè)方法就是Thread.isinterrupted()方法。
Thread.currentThread().isinterrupted()這個(gè)方法判斷的是Thread的普通成員,每個(gè)實(shí)例都有一個(gè)標(biāo)志位。
在我們以后就無(wú)腦使用Thread.currentThread().isInterrupted()方法,判斷線(xiàn)程是否中斷(標(biāo)志位)
線(xiàn)程的等待
在前面我們也介紹到j(luò)oin()方法,這個(gè)方法就是讓線(xiàn)程與線(xiàn)程之間,有了一定的執(zhí)行順序。我們知道在多線(xiàn)程中的調(diào)度是隨機(jī)的,不確定的,多線(xiàn)程的執(zhí)行靠調(diào)度器安排,該調(diào)度器的安排是隨機(jī)的,不規(guī)律的。其實(shí)線(xiàn)程等待就是一個(gè)行之有效的方法,實(shí)際上就是控制線(xiàn)程執(zhí)行的先后順序。
public class TestDemo22 {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> public static void main(String[] args) throws InterruptedException {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> Thread t = new Thread(()->{<!--{C}%3C!%2D%2D%20%2D%2D%3E--> for(int i = 0;i<5;i++){<!--{C}%3C!%2D%2D%20%2D%2D%3E--> System.out.println("hello thread"); try {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> Thread.sleep(1000); } catch (InterruptedException e) {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> e.printStackTrace(); } } }); t.start(); t.join();//main線(xiàn)程調(diào)用t.join()方法,main線(xiàn)程就處于阻塞狀態(tài),當(dāng)t線(xiàn)程執(zhí)行完后,喚醒main線(xiàn)程執(zhí)行后序代碼 System.out.println("hello main"); }}
獲取線(xiàn)程的引用
使用方法Thread.currentTread()就可以該線(xiàn)程的實(shí)例。
public class TestDemo23 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
//獲取當(dāng)前線(xiàn)程的引用
//System.out.println(Thread.currentThread().getName());
//因?yàn)楫?dāng)前使用的匿名內(nèi)部類(lèi)是繼承自Thread類(lèi),Thread就是該匿名內(nèi)部類(lèi)的父類(lèi),所以可以通過(guò)this得到當(dāng)前Thread的實(shí)例
System.out.println(this.getName());
}
};
t.start();
}
}

線(xiàn)程的休眠
其實(shí)線(xiàn)程休眠就是調(diào)用Thread.sleep()方法。
回顧之前的學(xué)習(xí)內(nèi)容,我們知道我們使用PCB描述一個(gè)進(jìn)程,使用雙向鏈表來(lái)組織進(jìn)程。這種說(shuō)法是針對(duì)一個(gè)進(jìn)程中只有一個(gè)線(xiàn)程的情況下。
那么如果一個(gè)進(jìn)程中有多個(gè)線(xiàn)程存在,那么每個(gè)線(xiàn)程就有一個(gè)PCB,那么每個(gè)進(jìn)程都會(huì)有一組PCB,
PCB上有一個(gè)字段為tgroupId,這個(gè)id就相當(dāng)于是進(jìn)程的id,進(jìn)程中的每個(gè)線(xiàn)程的tgroupId都是相同的。
那么進(jìn)程控制塊(process contral block)和線(xiàn)程有什么關(guān)系呢?
其實(shí)在linux中是不區(qū)分進(jìn)程和線(xiàn)程的,所謂的線(xiàn)程是程序員自己搞出來(lái)的,實(shí)際上linux只認(rèn)進(jìn)程控制塊(PCB),實(shí)際上線(xiàn)程就相當(dāng)于一個(gè)輕量級(jí)進(jìn)程。

到此這篇關(guān)于Java中Thread類(lèi)的使用和它的屬性的文章就介紹到這了,更多相關(guān)Java Thread類(lèi)使用和屬性?xún)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)微信掃碼登錄第三方網(wǎng)站功能(原理和代碼)
如何優(yōu)雅的拋出Spring Boot注解的異常詳解
JAVA按字節(jié)讀取文件的簡(jiǎn)單實(shí)例
基于Java編寫(xiě)簡(jiǎn)單的Excel工具類(lèi)
springboot項(xiàng)目部署到寶塔的詳細(xì)圖文教程
java中將一個(gè)List等分成n個(gè)list的工具方法(推薦)

