偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Java多線程知多少

開發(fā) 后端
進程是指可執(zhí)行程序存放在計算機存儲器的一個指令序列,它是一個動態(tài)執(zhí)行的過程。

 [[341158]]

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

什么是線程?

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

❝進程是指可執(zhí)行程序存放在計算機存儲器的一個指令序列,它是一個動態(tài)執(zhí)行的過程。❞

看到這個概念后,是不是感覺完全懵了,那我們該怎么理解呢?我們來想象一下平時我們使用電腦的一個場景。

 

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

 

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

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

 

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

 

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

❝線程是比進程還要小的運行單位,一個進程包含多個線程。❞

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

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

 

線程的創(chuàng)建

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

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

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

Thread類

Thread是一個線程類,位于java.lang包下

 

  • Thread類的常用方法

 

創(chuàng)建線程案例

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

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

代碼演示

  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();//啟動線程 
  14.     } 

運行結(jié)果

 

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

❝同時,還要注意一個線程只能執(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();//啟動線程 
  14.         mt.start(); 
  15.     } 

 

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

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

  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()+"正在運行"); 
  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();//啟動線程一 
  22.         mt2.start();//啟動線程二 
  23.     } 

運行結(jié)果

 

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

Runnable接口

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

創(chuàng)建線程案例

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

  • Java不支持多繼承

❝如果你的Class類已經(jīng)繼承了一個類,再去繼承Thread類是不可能的,所以這時候我們需要用接口去實現(xiàn),因為接口可以同時實現(xiàn)多個接口❞

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

❝我們知道,我們繼承一個類,就會繼承這個類中的所有方法,但對于線程來說,其實我們只需要重寫run()方法就可以,如果不打算重寫Thread類中的其他方法,我們也可以用使用接口的方式。從我們的實際應(yīng)用來看,使用實現(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()+"正在運行"+(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.     } 

運行結(jié)果

 

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

多個線程處理同一個資源的情況

  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()+"正在運行"+(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.     } 

運行結(jié)果

 

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

線程的狀態(tài)

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

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

線程的生命周期

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

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

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

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

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


 

 

結(jié)束

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

 

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

2013-07-15 15:35:06

2022-01-06 16:20:04

Java排序算法排序

2012-02-13 22:50:59

集群高可用

2024-08-06 10:07:15

2010-08-16 09:15:57

2025-04-14 08:50:00

Google ADK人工智能AI

2013-12-23 14:00:31

Windows 8.2Windows 8.1

2021-12-04 11:17:32

Javascript繼承編程

2017-07-14 10:51:37

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

2009-05-13 17:31:06

DBAOracleIT

2012-09-10 16:38:40

Windows Ser

2021-07-22 07:20:24

JS 遍歷方法前端

2018-08-31 10:53:25

MySQL存儲引擎

2018-12-12 15:01:22

開源存儲 軟件

2021-12-09 06:41:56

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

2013-08-02 09:42:37

BYODBYOC云存儲

2009-03-06 19:19:55

2022-05-08 18:02:11

tunnel隧道云原生

2024-07-01 12:30:09

2010-09-29 09:28:04

DHCP工作原理
點贊
收藏

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