自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java多線程知多少

開發(fā) 后端
進(jìn)程是指可執(zhí)行程序存放在計(jì)算機(jī)存儲(chǔ)器的一個(gè)指令序列,它是一個(gè)動(dòng)態(tài)執(zhí)行的過程。

 [[341158]]

本文轉(zhuǎn)載自微信公眾號(hào)「泰斗賢若如」,作者泰斗賢若如 。轉(zhuǎn)載本文請(qǐng)聯(lián)系泰斗賢若如公眾號(hào)。

什么是線程?

要說什么是「線程」,為了解釋這個(gè)概念,我首先要從「進(jìn)程」講起。來看一下「進(jìn)程」的概念:

❝進(jìn)程是指可執(zhí)行程序存放在計(jì)算機(jī)存儲(chǔ)器的一個(gè)指令序列,它是一個(gè)動(dòng)態(tài)執(zhí)行的過程。❞

看到這個(gè)概念后,是不是感覺完全懵了,那我們?cè)撛趺蠢斫饽?我們來想象一下平時(shí)我們使用電腦的一個(gè)場(chǎng)景。

 

我們平時(shí)敲代碼的時(shí)候是不是邊聽音樂邊敲代碼邊用QQ跟女朋友聊天啊(反正我是除了最后一條,前兩天都占了,嗚嗚)。音樂播放器、代碼編輯器、QQ這三個(gè)軟件是同時(shí)運(yùn)行的,這樣我們才能很多事情一起來完成,那么這三個(gè)軟件可以同時(shí)運(yùn)行,就是「進(jìn)程」在起作用。我們可以打開Windows的任務(wù)管理器,Windows的任務(wù)管理器中是可以看到有「進(jìn)程」這么一個(gè)選項(xiàng)卡。

 

打開windows任務(wù)管理器后,我們就能看到當(dāng)前操作系統(tǒng)中所運(yùn)行的所有進(jìn)程了。比如說上圖中我們看到的QQ的進(jìn)程、Google瀏覽器的進(jìn)程等等。而有的軟件可以有多個(gè)進(jìn)程,比如一些殺毒軟件、數(shù)據(jù)庫的軟件等。

其實(shí)早期的操作系統(tǒng)都是單任務(wù)的操作系統(tǒng),比如QQ,音樂播放器,它們只能單獨(dú)運(yùn)行,一個(gè)運(yùn)行之后,才能下一個(gè)運(yùn)行,比如大家想象一下,你先聽歌曲,聽完歌曲之后你才能在QQ中回復(fù)好友的問題,是不是感覺特別不方便啊。

 

而我們現(xiàn)在的操作系統(tǒng)都是多任務(wù)的操作系統(tǒng),可以多個(gè)程序同時(shí)運(yùn)行,我們可以邊聽歌邊回復(fù)信息。,這就是我們的進(jìn)程在起作用。

 

言歸正傳,我們現(xiàn)在說一下什么是線程:

❝線程是比進(jìn)程還要小的運(yùn)行單位,一個(gè)進(jìn)程包含多個(gè)線程。❞

比如說一個(gè)程序是由很多行代碼組成的,那么這些代碼中就可以分成很多塊放到不同的線程中,去分別執(zhí)行,所以我們認(rèn)為,「線程相當(dāng)于一個(gè)子程序」。

現(xiàn)在知道「進(jìn)程」和「線程」的概念之后,問題就來了,我們知道,程序的運(yùn)行是靠「CPU」來處理的,那如果你只有一個(gè)「CPU」的情況下,怎么能保證這些程序都能同時(shí)運(yùn)行呢?這里面我們可以想象成把「CPU」的執(zhí)行時(shí)間分成很多的小塊,每一小塊的時(shí)間都是固定的,我們可以把這個(gè)小塊叫「時(shí)間片」,時(shí)間片的時(shí)間可以非常短,比如說一毫秒,那么如果我們有音樂播放器、代碼編輯器、QQ三個(gè)軟件同時(shí)運(yùn)行,那么它們?nèi)齻€(gè)如何去獲取CPU的執(zhí)行時(shí)間呢?這個(gè)其實(shí)是隨機(jī)的,可以這樣考慮,我們的音樂播放器運(yùn)行一毫秒,然后它會(huì)把CPU的使用權(quán)轉(zhuǎn)給代碼編輯器,代碼編輯器運(yùn)行一毫秒將CPU的使用權(quán)轉(zhuǎn)給QQ,那么這些程序就輪流的在很短的時(shí)間內(nèi)使用CPU,對(duì)于CPU來講,這些軟件其實(shí)是輪流運(yùn)行的,但是由于它運(yùn)行的時(shí)間間隔非常的短,作為我們使用者來說,是感覺不到它的變化的,這樣我們就會(huì)認(rèn)為這些軟件都是同時(shí)運(yùn)行的,這就是為什么在只有一個(gè)CPU的情況下,這些軟件能夠同時(shí)運(yùn)行的原因,這個(gè)叫做時(shí)間片的輪轉(zhuǎn)。是通過對(duì)CPU的時(shí)間的輪轉(zhuǎn)來達(dá)到同時(shí)運(yùn)行的效果的。

 

線程的創(chuàng)建

線程創(chuàng)建有兩種方式:

  • 第一種:創(chuàng)建一個(gè)Thread類,或者一個(gè)Thread子類的對(duì)象。
  • 第二種:創(chuàng)建一個(gè)實(shí)現(xiàn)Runnable接口的類的對(duì)象。

這里面涉及到了一個(gè)Thread類和一個(gè)Runnable接口,我們來了解一下這兩個(gè)系統(tǒng)為我們定義的類和接口都有哪些屬性和方法:

Thread類

Thread是一個(gè)線程類,位于java.lang包下

 

  • Thread類的常用方法

 

創(chuàng)建線程案例

通過繼承Thread類的方式創(chuàng)建線程類,重寫run()方法

❝在這我提醒一下,對(duì)于很多初學(xué)者來說總有一個(gè)疑問,為什么它繼承Thread類就是一個(gè)線程了,這個(gè)我們說,Java中很多東西都是人家寫好了我們使用,Java中就是這么為大家規(guī)定的,你只有通過繼承Thread類、實(shí)現(xiàn)Runnable接口這兩種方式得到線程,所以我們按照這種方式做就可以了,這樣就能達(dá)到我們的目的。❞

代碼演示

  1. package com.thread; 
  2.  
  3. class MyThread extends Thread{ 
  4.     @Override 
  5.     public void run() { 
  6.         System.out.println("該線程正在執(zhí)行!"); 
  7.     } 
  8.  
  9. public class ThreadTest { 
  10.     public static void main(String[] args) { 
  11.  
  12.         MyThread mt = new MyThread(); 
  13.         mt.start();//啟動(dòng)線程 
  14.     } 

運(yùn)行結(jié)果

 

❝不知道大家從上面代碼中看出來什么沒有,我來強(qiáng)調(diào)一下,啟動(dòng)線程的時(shí)候,不是調(diào)用run()方法,我們以前學(xué)習(xí)的時(shí)候,執(zhí)行哪部分內(nèi)容就調(diào)用相對(duì)應(yīng)的方法,而在線程中,使用start()方法去啟動(dòng)線程,啟動(dòng)線程、執(zhí)行線程的時(shí)候執(zhí)行的是run()方法里面的代碼,這個(gè)是需要我們注意的。❞

❝同時(shí),還要注意一個(gè)線程只能執(zhí)行一次,也就是只能調(diào)用一次start()方法❞

  1. package com.thread; 
  2.  
  3. class MyThread extends Thread{ 
  4.     @Override 
  5.     public void run() { 
  6.         System.out.println("該線程正在執(zhí)行!"); 
  7.     } 
  8.  
  9. public class ThreadTest { 
  10.     public static void main(String[] args) { 
  11.  
  12.         MyThread mt = new MyThread(); 
  13.         mt.start();//啟動(dòng)線程 
  14.         mt.start(); 
  15.     } 

 

❝如圖,如果多次調(diào)用start()方法,就會(huì)拋出異常。❞

創(chuàng)建多個(gè)線程案例

  1. package com.thread; 
  2.  
  3. class MyThread extends Thread{ 
  4.  
  5.     public MyThread(String name){ 
  6.         super(name); 
  7.     } 
  8.     @Override 
  9.     public void run() { 
  10.         for (int i=0;i<=10;i++){ 
  11.             System.out.println(getName()+"正在運(yùn)行"); 
  12.         } 
  13.     } 
  14.  
  15. public class ThreadTest { 
  16.     public static void main(String[] args) { 
  17.  
  18.         MyThread mt1 = new MyThread("線程一"); 
  19.         MyThread mt2 = new MyThread("線程二"); 
  20.  
  21.         mt1.start();//啟動(dòng)線程一 
  22.         mt2.start();//啟動(dòng)線程二 
  23.     } 

運(yùn)行結(jié)果

 

❝在上面代碼中我創(chuàng)建了兩個(gè)線程,我們多運(yùn)行幾次,會(huì)發(fā)現(xiàn)每次的運(yùn)行結(jié)果都是不一樣的,從這里我們能體會(huì)到,線程要想去獲得CPU的使用權(quán),其實(shí)是隨機(jī)的,其結(jié)果會(huì)出現(xiàn)很多種不同的情況。❞

Runnable接口

  • 只有一個(gè)方法run()
  • Runnable是Java中用以實(shí)現(xiàn)線程的接口
  • 任何實(shí)現(xiàn)線程功能的類都必須實(shí)現(xiàn)該接口

創(chuàng)建線程案例

為什么要實(shí)現(xiàn)Runnable接口?

  • Java不支持多繼承

❝如果你的Class類已經(jīng)繼承了一個(gè)類,再去繼承Thread類是不可能的,所以這時(shí)候我們需要用接口去實(shí)現(xiàn),因?yàn)榻涌诳梢酝瑫r(shí)實(shí)現(xiàn)多個(gè)接口❞

  • 不打算重寫Thread類的其他方法

❝我們知道,我們繼承一個(gè)類,就會(huì)繼承這個(gè)類中的所有方法,但對(duì)于線程來說,其實(shí)我們只需要重寫run()方法就可以,如果不打算重寫Thread類中的其他方法,我們也可以用使用接口的方式。從我們的實(shí)際應(yīng)用來看,使用實(shí)現(xiàn)Runnable接口的方式應(yīng)用更廣泛一些。❞

代碼演示

  1. package com.thread; 
  2.  
  3. class PrintRunnable implements Runnable{ 
  4.  
  5.     @Override 
  6.     public void run() { 
  7.         int i=1; 
  8.  
  9.         while(i<=10){ 
  10.             System.out.println(Thread.currentThread().getName()+"正在運(yùn)行"+(i++)); 
  11.         } 
  12.  
  13.     } 
  14.  
  15. public class RunnableTest { 
  16.     public static void main(String[] args) { 
  17.  
  18.         PrintRunnable pr1 = new PrintRunnable(); 
  19.         Thread t1 = new Thread(pr1); 
  20.         t1.start(); 
  21.  
  22.         PrintRunnable pr2 = new PrintRunnable(); 
  23.         Thread t2 = new Thread(pr2); 
  24.         t2.start(); 
  25.  
  26.     } 

運(yùn)行結(jié)果

 

❝從上面的代碼中,我們發(fā)現(xiàn),用實(shí)現(xiàn)Runnable接口的形式去創(chuàng)建接口的時(shí)候,我們是三步走,先定義Runnable實(shí)現(xiàn)類的對(duì)象,然后通過它創(chuàng)建線程類的對(duì)象,最后啟動(dòng)線程,所以我們啟動(dòng)線程只能通過Thread類以及它的子類去啟動(dòng)。❞

多個(gè)線程處理同一個(gè)資源的情況

  1. package com.thread; 
  2.  
  3. class PrintRunnable implements Runnable{ 
  4.  
  5.     int i=1; 
  6.  
  7.     @Override 
  8.     public void run() { 
  9.  
  10.         while(i<=10){ 
  11.             System.out.println(Thread.currentThread().getName()+"正在運(yùn)行"+(i++)); 
  12.         } 
  13.  
  14.     } 
  15.  
  16. public class RunnableTest { 
  17.     public static void main(String[] args) { 
  18.  
  19.         PrintRunnable pr = new PrintRunnable(); 
  20.         Thread t1 = new Thread(pr); 
  21.         t1.start(); 
  22.         Thread t2 = new Thread(pr); 
  23.         t2.start(); 
  24.  
  25.     } 

運(yùn)行結(jié)果

 

❝出現(xiàn)上面這種運(yùn)行結(jié)果,原因是run()方法被多個(gè)線程共享,多個(gè)線程也就是Thread類的實(shí)例,也就是pr對(duì)象被t1和t2兩個(gè)Thread類的實(shí)例共享,這適用于多個(gè)線程處理同一個(gè)資源的情。i相當(dāng)于一個(gè)資源,t1和t2共享了這個(gè)資源。❞

線程的狀態(tài)

  • 新建(New)
  • 可運(yùn)行(Runnable)
  • 正在運(yùn)行(Running)
  • 阻塞(Blocked)
  • 終止(Dead)

❝線程的狀態(tài)首先是「新建狀態(tài)」,創(chuàng)建一個(gè)Thread或者Thread子類的對(duì)象的時(shí)候,線程就進(jìn)入了「新建狀態(tài)」。接下來是「可運(yùn)行狀態(tài)」,當(dāng)已經(jīng)創(chuàng)建好的線程對(duì)象調(diào)用start()方法,這時(shí)候就進(jìn)入了「可運(yùn)行狀態(tài)」,在前面我也說過,線程什么時(shí)候運(yùn)行是由CPU來決定的,只有當(dāng)線程獲取CPU的使用權(quán)的時(shí)候,它才能執(zhí)行。所以這里面,線程調(diào)用start()方法之后,不是馬上進(jìn)入「運(yùn)行狀態(tài)」,而是進(jìn)入「可運(yùn)行狀態(tài)」,這種狀態(tài)也叫「就緒狀態(tài)」,也就是我已經(jīng)準(zhǔn)備好了。接下來就是「正在運(yùn)行狀態(tài)」,一個(gè)處于「可運(yùn)行狀態(tài)」的線程,一旦獲取了CPU的使用權(quán),就可以立即進(jìn)入「正在運(yùn)行狀態(tài)」,接下來就是「阻塞狀態(tài)」,當(dāng)線程遇到一些干擾的時(shí)候,它將進(jìn)入「阻塞狀態(tài)」,也就是它不再執(zhí)行,后面我在說「生命周期」的時(shí)候也會(huì)詳細(xì)說「如何進(jìn)入阻塞狀態(tài)」,最后是「終止?fàn)顟B(tài)」,這就是線程的五個(gè)狀態(tài)。❞

線程的生命周期

所謂的線程的「生命周期」,就是線程從「創(chuàng)建」到「啟動(dòng)」,直至「運(yùn)行結(jié)束」的這段時(shí)間我們就叫它的「生命周期」。其實(shí)線程的生命周期就是我上面說到的五個(gè)狀態(tài)相互的轉(zhuǎn)換過程,可以通過調(diào)用Thread類的一些相關(guān)方法來影響線程的狀態(tài),「狀態(tài)之間的轉(zhuǎn)換」就可以構(gòu)成我們最終的「生命周期」了。

首先是「新建狀態(tài)」,只要你創(chuàng)建了一個(gè)Thread或者Thread子類的對(duì)象,你就進(jìn)入了「新建狀態(tài)」,然后通過調(diào)用線程的start()方法去啟動(dòng)線程,進(jìn)入「可運(yùn)行狀態(tài)」,當(dāng)處于「可運(yùn)行狀態(tài)」的線程獲取CPU使用權(quán)后,它將進(jìn)入「正在運(yùn)行狀態(tài)」,如果一旦CPU的使用權(quán)到時(shí)間了,那么線程就會(huì)從「正在運(yùn)行狀態(tài)」重新回到「可運(yùn)行狀態(tài)」,去等待獲取下一次的CPU使用權(quán),所以這塊從「正在運(yùn)行狀態(tài)」到「可運(yùn)行狀態(tài)」的一個(gè)條件就是「時(shí)間片」用完,還有一個(gè)就是調(diào)用yield()這樣一個(gè)方法,去從「運(yùn)行狀態(tài)」變成「可運(yùn)行狀態(tài)」。

那么再來看一下,「正在運(yùn)行狀態(tài)」到「阻塞狀態(tài)」之間的轉(zhuǎn)換,可以通過調(diào)用Thread類中的一些方法來完成,比如說join()方法、wait()方法、sleep()方法,這些都可以把線程從「正在運(yùn)行狀態(tài)」轉(zhuǎn)換成「阻塞狀態(tài)」,在后面我也會(huì)說到這些方法的使用。還有一種情況就是I/O請(qǐng)求,I是Input,O是Output,就是輸入輸出請(qǐng)求的意思?!缸枞麪顟B(tài)」我們可以看做一個(gè)正在運(yùn)行的線程進(jìn)入了一個(gè)暫停的狀態(tài),I/O請(qǐng)求需要耗費(fèi)一定的時(shí)間,這時(shí)候就可以讓線程進(jìn)入「阻塞狀態(tài)」,等待I/O請(qǐng)求完成,再繼續(xù)進(jìn)行執(zhí)行。這是「正在運(yùn)行狀態(tài)」到「阻塞狀態(tài)」的轉(zhuǎn)換,反過來,「阻塞狀態(tài)」的線程是不能直接轉(zhuǎn)換成「正在運(yùn)行狀態(tài)」的,因?yàn)槲抑罢f過要獲取CPU的使用權(quán)才能變成「正在運(yùn)行狀態(tài)」。

所以這里「阻塞狀態(tài)」最終會(huì)轉(zhuǎn)換成「可運(yùn)行狀態(tài)」,那么它要滿足什么條件呢?這個(gè)條件和之前說的「正在運(yùn)行狀態(tài)」變成「阻塞狀態(tài)」的條件是對(duì)應(yīng)的,也就是解決這些問題的方法。第一個(gè)是等待調(diào)用join()方法的線程執(zhí)行完畢,第二個(gè)是調(diào)用notify()方法或notifyAll()方法,這兩個(gè)方法其實(shí)是對(duì)應(yīng)wait()方法使用的,也就是調(diào)用wait()方法的線程必須調(diào)用notify()方法或notifyAll()方法才能進(jìn)入「可運(yùn)行狀態(tài)」,這個(gè)我會(huì)在后面說同步的時(shí)候詳細(xì)說。再有就是之前調(diào)用sleep()方法進(jìn)行阻塞的線程,當(dāng)你「休眠超時(shí)」以后,它就會(huì)重新變成「可運(yùn)行狀態(tài)」。最后一點(diǎn)就是I/O請(qǐng)求完成,一旦I/O請(qǐng)求完成,那么線程依然可以回到「可運(yùn)行狀態(tài)」。

最后,我們看到還有一個(gè)狀態(tài)是「終止?fàn)顟B(tài)」,線程什么時(shí)候進(jìn)入「終止?fàn)顟B(tài)」呢?對(duì)于一個(gè)新建的線程來說,如果它去調(diào)用stop()方法,會(huì)進(jìn)入「終止?fàn)顟B(tài)」,同樣,對(duì)于一個(gè)「可運(yùn)行狀態(tài)」的線程和一個(gè)處于「阻塞狀態(tài)」的線程,它們調(diào)用stop()方法也會(huì)進(jìn)入「終止?fàn)顟B(tài)」。在這我提一下,對(duì)于這個(gè)stop()方法,其實(shí)已經(jīng)不建議使用了,因?yàn)镴ava的JDK當(dāng)中會(huì)顯示「版本過期」,不推薦使用了。那么一個(gè)處在「正在運(yùn)行狀態(tài)」的線程如何進(jìn)入「終止?fàn)顟B(tài)」呢?這里有幾種情況,第一種當(dāng)然也是調(diào)用stop()方法,再有就是一個(gè)線程執(zhí)行完畢了以后或者是一個(gè)正在執(zhí)行的線程因?yàn)槟承┰虍惓=K止了,整個(gè)程序都終止了,它也會(huì)進(jìn)入「終止?fàn)顟B(tài)」,就跟一個(gè)人的生命一樣,從出生,然后長大,最后結(jié)束這樣的一個(gè)過程。


 

 

結(jié)束

講到這,關(guān)于線程的知識(shí)點(diǎn)也講清楚了,從線程的創(chuàng)建到使用,再到線程的狀態(tài)到生命周期,我都用通俗易懂的話語做了解釋,希望能對(duì)讀者朋友們有所幫助。

 

責(zé)任編輯:武曉燕 來源: 泰斗賢若如
相關(guān)推薦

2013-07-15 15:35:06

2022-01-06 16:20:04

Java排序算法排序

2024-08-06 10:07:15

2012-02-13 22:50:59

集群高可用

2010-08-16 09:15:57

2013-12-23 14:00:31

Windows 8.2Windows 8.1

2021-12-04 11:17:32

Javascript繼承編程

2025-04-14 08:50:00

Google ADK人工智能AI

2017-07-14 10:51:37

性能優(yōu)化SQL性能分析

2010-09-29 09:28:04

DHCP工作原理

2018-12-12 15:01:22

開源存儲(chǔ) 軟件

2024-07-01 12:30:09

2013-08-02 09:42:37

BYODBYOC云存儲(chǔ)

2021-12-09 06:41:56

Python協(xié)程多并發(fā)

2022-05-08 18:02:11

tunnel隧道云原生

2009-03-06 19:19:55

2009-05-13 17:31:06

DBAOracleIT

2018-08-31 10:53:25

MySQL存儲(chǔ)引擎

2021-07-22 07:20:24

JS 遍歷方法前端

2012-09-10 16:38:40

Windows Ser
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)