當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 華清遠(yuǎn)見(jiàn)武漢中心
[導(dǎo)讀]線程(英語(yǔ):Thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)。在Unix System V及SunOS中也被稱為輕量進(jìn)程(Lightweight Processes),但輕量進(jìn)程更多指內(nèi)核線程(Kernel Thread),而把用戶線程(User Thread)稱為線程。


線程


線程的概念,百度是這樣解釋的:

線程(英語(yǔ):Thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)。在Unix System V及SunOS中也被稱為輕量進(jìn)程(Lightweight Processes),但輕量進(jìn)程更多指內(nèi)核線程(Kernel Thread),而把用戶線程(User Thread)稱為線程。

1.1 線程與進(jìn)程的區(qū)別


進(jìn)程: 指在系統(tǒng)中正在運(yùn)行 的一個(gè)應(yīng) 用程序;程 序一旦運(yùn)行就是進(jìn)程;進(jìn)程——資源分配的最小單位。

線程:系統(tǒng)分配處理器時(shí)間資源的基本單元,或者說(shuō)進(jìn)程之內(nèi)獨(dú)立執(zhí)行的一個(gè)單元執(zhí)行流。線程——程序執(zhí)行的最小單位。 也就是,進(jìn)程可以包含多個(gè)線程,而線程是程序執(zhí)行的最小單位。

1.2 線程的狀態(tài)


  • NEW: 線程剛創(chuàng)建
  • RUNNABLE: 在JVM中正在運(yùn)行的線程,其中運(yùn)行狀態(tài)可以有運(yùn)行中RUNNING和READY兩種狀態(tài),由系統(tǒng)調(diào)度進(jìn)行狀態(tài)改變。
  • BLOCKED:線程處于阻塞狀態(tài),等待監(jiān)視鎖,可以重新進(jìn)行同步代碼塊中執(zhí)行
  • WAITING : 等待狀態(tài)
  • TIMED_WAITING: 調(diào)用sleep() join() wait()方法可能導(dǎo)致線程處于等待狀態(tài)
  • TERMINATED: 線程執(zhí)行完畢,已經(jīng)退出


1.3 Notify和Wait :


Notify和Wait 的作用

首先看源碼給出的解釋,這里翻譯了一下: Notify: 喚醒一個(gè)正在等待這個(gè)對(duì)象的線程監(jiān)控。如果有任何線程正在等待這個(gè)對(duì)象,那么它們中的一個(gè)被選擇被喚醒。選擇是任意的,發(fā)生在執(zhí)行的酌情權(quán)。一個(gè)線程等待一個(gè)對(duì)象通過(guò)調(diào)用一個(gè){@code wait}方法進(jìn)行監(jiān)視。 Notify()需要在同步方法或同步塊中調(diào)用,即在調(diào)用前,線程也必須獲得該對(duì)象的對(duì)象級(jí)別鎖 Wait: 導(dǎo)致當(dāng)前線程等待,直到另一個(gè)線程調(diào)用 {@link java.lang.Object#notify()}方法或 {@link java.lang.Object#notifyAll()}方法。 換句話說(shuō),這個(gè)方法的行為就像它簡(jiǎn)單一樣 執(zhí)行調(diào)用{@code wait(0)}。 當(dāng)前線程必須擁有該對(duì)象的監(jiān)視器。 線程 釋放此監(jiān)視器的所有權(quán),并等待另一個(gè)線程 通知等待該對(duì)象的監(jiān)視器的線程,喚醒 通過(guò)調(diào)用{@code notify}方法或 {@code notifyAll}方法。然后線程等待,直到它可以重新取得監(jiān)視器的所有權(quán),然后繼續(xù)執(zhí)行。 Wait()的作用是使當(dāng)前執(zhí)行代碼的線程進(jìn)行等待,它是Object類的方法,該方法用來(lái)將當(dāng)前線程置入預(yù)執(zhí)行隊(duì)列中,并且在Wait所在的代碼行處停止執(zhí)行,直到接到通知或被中斷為止。 在調(diào)用Wait方法之前,線程必須獲得該對(duì)象的對(duì)象級(jí)別鎖,即只能在同步方法或同步塊中調(diào)用Wait方法。 Wait和Sleep的區(qū)別:

  • 它們最大本質(zhì)的區(qū)別是,Sleep()不釋放同步鎖,Wait()釋放同步鎖。
  • 還有用法的上的不同是:Sleep(milliseconds)可以用時(shí)間指定來(lái)使他自動(dòng)醒過(guò)來(lái),如果時(shí)間不到你只能調(diào)用Interreput()來(lái)強(qiáng)行打斷;Wait()可以用Notify()直接喚起。
  • 這兩個(gè)方法來(lái)自不同的類分別是Thread和Object
  • 最主要是Sleep方法沒(méi)有釋放鎖,而Wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。


1.4 Thread.sleep() 和Thread.yield()的異同


  • 相同 :Sleep()和yield()都會(huì)釋放CPU。
  • 不同:Sleep()使當(dāng)前線程進(jìn)入停滯狀態(tài),所以執(zhí)行Sleep()的線程在指定的時(shí)間內(nèi)肯定不會(huì)執(zhí)行;yield()只是使當(dāng)前線程重新回到可執(zhí)行狀態(tài),所以執(zhí)行yield()的線程有可能在進(jìn)入到可執(zhí)行狀態(tài)后馬上又被執(zhí)行。 S leep()可使優(yōu)先級(jí)低的線程得到執(zhí)行的機(jī)會(huì),當(dāng)然也可以讓同優(yōu)先級(jí)和高優(yōu)先級(jí)的線程有執(zhí)行的機(jī)會(huì);yield()只 能使同優(yōu)先級(jí)的線程有執(zhí)行的機(jī)會(huì)。


1.5 補(bǔ)充:死鎖的概念


死鎖:指兩個(gè)或兩個(gè)以上的進(jìn)程(或線程)在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程。

死鎖產(chǎn)生的四個(gè)必要條件(缺一不可):

  • 互斥條件:顧名思義,線程對(duì)資源的訪問(wèn)是排他性,當(dāng)該線程釋放資源后下一線程才可進(jìn)行占用。
  • 請(qǐng)求和保持:簡(jiǎn)單來(lái)說(shuō)就是自己拿的不放手又等待新的資源到手。 線程T1至少已經(jīng)保持了一個(gè)資源R1占用,但又提出對(duì)另一個(gè)資源R2請(qǐng)求,而此時(shí),資源R2被其他線程T2占用,于是該線程T1也必須等待,但又對(duì)自己保持的資源R1不釋放。
  • 不可剝奪:在沒(méi)有使用完資源時(shí),其他線性不能進(jìn)行剝奪。
  • 循環(huán)等待:一直等待對(duì)方線程釋放資源。

我們可以根據(jù)死鎖的四個(gè)必要條件破壞死鎖的形成。

1.6 補(bǔ)充:并發(fā)和并行的區(qū)別


并發(fā):是指在某個(gè)時(shí)間段內(nèi),多任務(wù)交替的執(zhí)行任務(wù)。當(dāng)有多個(gè)線程在操作時(shí),把CPU運(yùn)行時(shí)間劃分成若干個(gè)時(shí)間段,再將時(shí)間段分配給各個(gè)線程執(zhí)行。在一個(gè)時(shí)間段的線程代碼運(yùn)行時(shí),其它線程處于掛起狀。

并行:是指同一時(shí)刻同時(shí)處理多任務(wù)的能力。當(dāng)有多個(gè)線程在操作時(shí),CPU同時(shí)處理這些線程請(qǐng)求的能力。 區(qū)別就在于CPU是否能同時(shí)處理所有任務(wù),并發(fā)不能,并行能。

1.7 補(bǔ)充:線程安全三要素


  • 原子性:Atomic包、CAS算法、Synchronized、Lock。
  • 可見(jiàn)性:Synchronized、Volatile(不能保證原子性)。
  • 有序性:Happens-before規(guī)則。


1.8 補(bǔ)充:如何實(shí)現(xiàn)線程安全


  • 互斥同步:Synchronized、Lock。
  • 非阻塞同步:CAS。
  • 無(wú)需同步的方案:如果一個(gè)方法本來(lái)就不涉及共享數(shù)據(jù),那它自然就無(wú)需任何同步操作去保證正確性。


1.9 補(bǔ)充:保證線程安全的機(jī)制:


  • Synchronized關(guān)鍵字
  • L ock
  • CAS、原子變量
  • ThreadLocl:簡(jiǎn)單來(lái)說(shuō)就是讓每個(gè)線程,對(duì)同一個(gè)變量,都有自己的獨(dú)有副本,每個(gè)線程實(shí)際訪問(wèn)的對(duì)象都是自己的,自然也就不存在線程安全問(wèn)題了。
  • Volatile
  • CopyOnWrite寫(xiě)時(shí)復(fù)制

隨著CPU核心的增多以及互聯(lián)網(wǎng)迅速發(fā)展,單線程的程序處理速度越來(lái)越跟不上發(fā)展速度和大數(shù)據(jù)量的增長(zhǎng)速度,多線程應(yīng)運(yùn)而生,充分利用CPU資源的同時(shí),極大提高了程序處理速度。

創(chuàng)建線程的方法


繼承Thread類:

    
public  class ThreadCreateTest {
     public static void main(String[] args) {
         new MyThread().start();
    }
}

class MyThread extends Thread {
     @Override
     public void run() {
        System.out.println(Thread.currentThread().getName() +  "\t" + Thread.currentThread().getId());
    }
}

實(shí)現(xiàn)Runable接口:

    
public  class RunableCreateTest {
     public static void main(String[] args) {
        MyRunnable runnable =  new MyRunnable();
         new Thread(runnable).start();
    }
}

class MyRunnable implements Runnable {
     @Override
     public void run() {
        System.out.println(Thread.currentThread().getName() +  "\t" + Thread.currentThread().getId());
    }
}

通過(guò)Callable和Future創(chuàng)建線程:

    
public  class CallableCreateTest {
     public static void main(String[] args) throws Exception {
          // 將Callable包裝成FutureTask,F(xiàn)utureTask也是一種Runnable
        MyCallable callable =  new MyCallable();
        FutureTask futureTask =  new FutureTask<>(callable);
         new Thread(futureTask).start();

         // get方法會(huì)阻塞調(diào)用的線程
        Integer sum = futureTask.get();
        System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() +  "=" + sum);
    }
}


class MyCallable implements Callable<Integer{

     @Override
     public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName() +  "\t" + Thread.currentThread().getId() +  "\t" +  new Date() +  " \tstarting...");

         int sum =  0;
         for ( int i =  0; i <=  100000; i++) {
            sum += i;
        }
        Thread.sleep( 5000);

        System.out.println(Thread.currentThread().getName() +  "\t" + Thread.currentThread().getId() +  "\t" +  new Date() +  " \tover...");
         return sum;
    }
}

線程池方式創(chuàng)建: 實(shí)現(xiàn)Runnable接口這種方式更受歡迎,因?yàn)檫@不需要繼承Thread類。在應(yīng)用設(shè)計(jì)中已經(jīng)繼承了別的對(duì)象的情況下,這需要多繼承(而Java不支持多繼承,但可以多實(shí)現(xiàn)?。荒軐?shí)現(xiàn)接口。同時(shí),線程池也是非常高效的,很容易實(shí)現(xiàn)和使用。 實(shí)際開(kāi)發(fā)中,阿里巴巴開(kāi)發(fā)插件一直提倡使用線程池創(chuàng)建線程,原因在下方會(huì)解釋,所以上面的代碼我就只簡(jiǎn)寫(xiě)了一些Demo。

2.1 線程池創(chuàng)建線程


線程池,顧名思義,線程存放的地方。和數(shù)據(jù)庫(kù)連接池一樣,存在的目的就是為了較少系統(tǒng)開(kāi)銷,主要由以下幾個(gè)特點(diǎn):

降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗(主要)。 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。 提高線程的可管理性。線程是稀缺資源,如果無(wú)限制地創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性。 Java提供四種線程池創(chuàng)建方式:

  • newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程。
  • newFixedThreadPool創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。
  • newScheduledThreadPool創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
  • newSingleThreadExecutor創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。

通過(guò)源碼我們得知ThreadPoolExecutor繼承自AbstractExecutorService,而AbstractExecutorService實(shí)現(xiàn)了ExecutorService。

    
public  class ThreadPoolExecutor extends AbstractExecutorService

public  abstract  class AbstractExecutorService implements ExecutorService

2.2 ThreadPoolExecutor介紹


實(shí)際項(xiàng)目中,用的最多的就是ThreadPoolExecutor這個(gè)類,而《阿里巴巴Java開(kāi)發(fā)手冊(cè)》中強(qiáng)制線程池不允許使用Executors去創(chuàng)建,而是通過(guò)New ThreadPoolExecutor實(shí)例的方式,這樣的處理方式讓寫(xiě)的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。

我們從ThreadPoolExecutor入手多線程創(chuàng)建方式,先看一下線程池創(chuàng)建的最全參數(shù)。

    
     public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler
{
         if (corePoolSize <  0 ||
            maximumPoolSize <=  0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime <  0)
             throw  new IllegalArgumentException();
         if (workQueue ==  null || threadFactory ==  null || handler ==  null)
             throw  new NullPointerException();
         this.corePoolSize = corePoolSize;
         this.maximumPoolSize = maximumPoolSize;
         this.workQueue = workQueue;
         this.keepAliveTime = unit.toNanos(keepAliveTime);
         this.threadFactory = threadFactory;
         this.handler = handler;
    }

參數(shù)說(shuō)明如下:

  • corePoolSize:線程池的核心線程數(shù),即便線程池里沒(méi)有任何任務(wù),也會(huì)有corePoolSize個(gè)線程在候著等任務(wù)。
  • maximumPoolSize:最大線程數(shù),不管提交多少任務(wù),線程池里最多工作線程數(shù)就是maximumPoolSize。
  • keepAliveTime:線程的存活時(shí)間。當(dāng)線程池里的線程數(shù)大于corePoolSize時(shí),如果等了keepAliveTime時(shí)長(zhǎng)還沒(méi)有任務(wù)可執(zhí)行,則線程退出。
  • Unit:這個(gè)用來(lái)指定keepAliveTime的單位,比如秒:TimeUnit.SECONDS。
  • BlockingQueue:一個(gè)阻塞隊(duì)列,提交的任務(wù)將會(huì)被放到這個(gè)隊(duì)列里。
  • threadFactory:線程工廠,用來(lái)創(chuàng)建線程,主要是為了給線程起名字,默認(rèn)工廠的線程名字:pool-1-thread-3。
  • handler:拒絕策略,當(dāng)線程池里線程被耗盡,且隊(duì)列也滿了的時(shí)候會(huì)調(diào)用。

2.2.1BlockingQueue


對(duì)于BlockingQueue個(gè)人感覺(jué)還需要單獨(dú)拿出來(lái)說(shuō)一下。

BlockingQueue:阻塞隊(duì)列,有先進(jìn)先出(注重公平性)和先進(jìn)后出(注重時(shí)效性)兩種,常見(jiàn)的有兩種阻塞隊(duì)列:ArrayBlockingQueue和LinkedBlockingQueue 隊(duì)列的數(shù)據(jù)結(jié)構(gòu)大致如圖: 隊(duì)列一端進(jìn)入,一端輸出。而當(dāng)隊(duì)列滿時(shí),阻塞。BlockingQueue核心方法:1. 放入數(shù)據(jù)put2. 獲取數(shù)據(jù)take。常見(jiàn)的兩種Queue:

2.2.2 ArrayBlockingQueue


基于數(shù)組實(shí)現(xiàn),在ArrayBlockingQueue內(nèi)部,維護(hù)了一個(gè)定長(zhǎng)數(shù)組,以便緩存隊(duì)列中的數(shù)據(jù)對(duì)象,這是一個(gè)常用的阻塞隊(duì)列,除了一個(gè)定長(zhǎng)數(shù)組外,ArrayBlockingQueue內(nèi)部還保存著兩個(gè)整形變量,分別標(biāo)識(shí)著隊(duì)列的頭部和尾部在數(shù)組中的位置。

一段代碼來(lái)驗(yàn)證一下:

    
    package  map;

     import java.util.concurrent.*;

     public  class MyTestMap {
         // 定義阻塞隊(duì)列大小
         private  static final  int maxSize =  5;
         public static void main(String[] args){
            ArrayBlockingQueue  queue =  new ArrayBlockingQueue(maxSize);
             new Thread( new Productor( queue)).start();
             new Thread( new Customer( queue)).start();
        }
    }

     class Customer implements Runnable {
         private BlockingQueue  queue;
        Customer(BlockingQueue  queue) {
             this. queue =  queue;
        }

        @ Override
        public void run() 
{
             this.cusume();
        }

         private void cusume() {
             while ( true) {
                 try {
                     int count = ( intqueue.take();
                    System.out.println( "customer正在消費(fèi)第" + count +  "個(gè)商品===");
                     // 只是為了方便觀察輸出結(jié)果
                    Thread.sleep( 10);
                }  catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

     class Productor implements Runnable {
         private BlockingQueue  queue;
         private  int count =  1;
        Productor(BlockingQueue  queue) {
             this. queue =  queue;
        }

        @ Override
        public void run() 
{
             this.product();
        }
         private void product() {
             while ( true) {
                 try {
                     queue.put(count);
                    System.out.println( "生產(chǎn)者正在生產(chǎn)第" + count +  "個(gè)商品");
                    count++;
                }  catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

//輸出如下
/**
生產(chǎn)者正在生產(chǎn)第1個(gè)商品
生產(chǎn)者正在生產(chǎn)第2個(gè)商品
生產(chǎn)者正在生產(chǎn)第3個(gè)商品
生產(chǎn)者正在生產(chǎn)第4個(gè)商品
生產(chǎn)者正在生產(chǎn)第5個(gè)商品
customer正在消費(fèi)第1個(gè)商品===
*/


2.2.3 LinkedBlockingQueue


基于鏈表的阻塞隊(duì)列,內(nèi)部也維護(hù)了一個(gè)數(shù)據(jù)緩沖隊(duì)列。需要我們注意的是如果構(gòu)造一個(gè)LinkedBlockingQueue對(duì)象,而沒(méi)有指定其容量大小。

LinkedBlockingQueue會(huì)默認(rèn)一個(gè)類似無(wú)限大小的容量(Integer.MAX_VALUE),這樣的話,如果生產(chǎn)者的速度一旦大于消費(fèi)者的速度,也許還沒(méi)有等到隊(duì)列滿阻塞產(chǎn)生,系統(tǒng)內(nèi)存就有可能已被消耗殆盡了。

2.2.4 LinkedBlockingQueue和ArrayBlockingQueue的主要區(qū)別


  • ArrayBlockingQueue的初始化必須傳入隊(duì)列大小,LinkedBlockingQueue則可以不傳入。
  • ArrayBlockingQueue用一把鎖控制并發(fā),LinkedBlockingQueue倆把鎖控制并發(fā),鎖的細(xì)粒度更細(xì)。即前者生產(chǎn)者消費(fèi)者進(jìn)出都是一把鎖,后者生產(chǎn)者生產(chǎn)進(jìn)入是一把鎖,消費(fèi)者消費(fèi)是另一把鎖。
  • ArrayBlockingQueue采用數(shù)組的方式存取,LinkedBlockingQueue用Node鏈表方式存取。


2.2.5handler拒絕策略


Java提供了4種丟棄處理的方法,當(dāng)然你也可以自己實(shí)現(xiàn),主要是要實(shí)現(xiàn)接口:RejectedExecutionHandler中的方法。

  • AbortPolicy:不處理,直接拋出異常。
  • CallerRunsPolicy:只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù),即提交任務(wù)的線程。
  • DiscardOldestPolicy:LRU策略,丟棄隊(duì)列里最近最久不使用的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。
  • DiscardPolicy:不處理,丟棄掉,不拋出異常。


2.2.6線程池五種狀態(tài)


    
     private  static  final  int RUNNING    = - 1 << COUNT_BITS;
     private  static  final  int SHUTDOWN   =   0 << COUNT_BITS;
     private  static  final  int STOP       =   1 << COUNT_BITS;
     private  static  final  int TIDYING    =   2 << COUNT_BITS;
     private  static  final  int TERMINATED =   3 << COUNT_BITS;

RUNNING:在這個(gè)狀態(tài)的線程池能判斷接受新提交的任務(wù),并且也能處理阻塞隊(duì)列中的任務(wù)。

SHUTDOWN:處于關(guān)閉的狀態(tài),該線程池不能接受新提交的任務(wù),但是可以處理阻塞隊(duì)列中已經(jīng)保存的任務(wù),在線程處于RUNNING狀態(tài),調(diào)用shutdown()方法能切換為該狀態(tài)。

STOP:線程池處于該狀態(tài)時(shí)既不能接受新的任務(wù)也不能處理阻塞隊(duì)列中的任務(wù),并且能中斷現(xiàn)在線程中的任務(wù)。當(dāng)線程處于RUNNING和SHUTDOWN狀態(tài),調(diào)用shutdownNow()方法就可以使線程變?yōu)樵摖顟B(tài)。

TIDYING:在SHUTDOWN狀態(tài)下阻塞隊(duì)列為空,且線程中的工作線程數(shù)量為0就會(huì)進(jìn)入該狀態(tài),當(dāng)在STOP狀態(tài)下時(shí),只要線程中的工作線程數(shù)量為0就會(huì)進(jìn)入該狀態(tài)。

TERMINATED:在TIDYING狀態(tài)下調(diào)用terminated()方法就會(huì)進(jìn)入該狀態(tài)??梢哉J(rèn)為該狀態(tài)是最終的終止?fàn)顟B(tài)。

回到線程池創(chuàng)建ThreadPoolExecutor,我們了解了這些參數(shù),再來(lái)看看ThreadPoolExecutor的內(nèi)部工作原理:

  • 判斷核心線程是否已滿 ,是進(jìn)入隊(duì)列,否:創(chuàng)建線程
  • 判斷等待隊(duì)列是否已滿,是:查看線程池是否已滿,否:進(jìn)入等待隊(duì)列
  • 查看線程池是否已滿,是:拒絕,否創(chuàng)建線程


2.3深入理解ThreadPoolExecutor


進(jìn)入Execute方法可以看到:

    
   public void execute(Runnable command{
         if (command ==  null)
             throw  new NullPointerException();
         int c = ctl. get();
       //判斷當(dāng)前活躍線程數(shù)是否小于corePoolSize,如果小于,則調(diào)用addWorker創(chuàng)建線程執(zhí)行任務(wù)
         if (workerCountOf(c) < corePoolSize) {
             if (addWorker(command,  true))
                 return;
            c = ctl. get();
        }
       //如果不小于corePoolSize,則將任務(wù)添加到workQueue隊(duì)列。
         if (isRunning(c) && workQueue.offer(command)) {
             int recheck = ctl. get();
             if (! isRunning(recheck) &&  remove(command))
                reject(command);
             else  if (workerCountOf(recheck) ==  0)
                addWorker( nullfalse);
        }
       //如果放入workQueue失敗,則創(chuàng)建線程執(zhí)行任務(wù),如果這時(shí)創(chuàng)建線程失敗(當(dāng)前線程數(shù)不小于maximumPoolSize時(shí)),就會(huì)調(diào)用reject(內(nèi)部調(diào)用handler)拒絕接受任務(wù)。
         else  if (!addWorker(command,  false))
            reject(command);
    }

AddWorker方法:

  • 創(chuàng)建Worker對(duì)象,同時(shí)也會(huì)實(shí)例化一個(gè)Thread對(duì)象。 在創(chuàng)建Worker時(shí)會(huì)調(diào)用threadFactory來(lái)創(chuàng)建一個(gè)線程。
  • 然后 啟動(dòng)這個(gè)線程。


2.3.1線程池中CTL屬性的作用是什么?


看源碼第一反應(yīng)就是這個(gè)CTL到底是個(gè)什么東東?有啥用?一番研究得出如下結(jié)論:

CTL屬性包含兩個(gè)概念:

    
     private  final AtomicInteger ctl =  new AtomicInteger(ctlOf(RUNNING,  0));

     private static int ctlOf(int rs, int wc) return rs | wc; }

  • runState:即rs 表明當(dāng)前線程池的狀態(tài),是否處于Running,Shutdown,Stop,Tidying。
  • workerCount:即wc表明當(dāng)前有效的線程數(shù)。

我們點(diǎn)擊workerCount即工作狀態(tài)記錄值,以RUNNING為例,RUNNING = -1 << COUNT_BITS;,即-1無(wú)符號(hào)左移COUNT_BITS位,進(jìn)一步我們得知COUNT_BITS位29,因?yàn)镮nteger位數(shù)為31位(2的五次方減一)

    
     private  static  final  int COUNT_BITS = Integer.SIZE -  3;

既然是29位那么就是Running的值為:

    
1110  0000  0000  0000  0000  0000  0000  0000 
|||
31~29位

那低28位呢,就是記錄當(dāng)前線程的總線數(shù)啦:

    
     // Packing and unpacking ctl
     private static int runStateOf(int c)     return c & ~CAPACITY; }
     private static int workerCountOf(int c)  return c & CAPACITY; }
     private static int ctlOf(int rs, int wc) return rs | wc; }

從上述代碼可以看到workerCountOf這個(gè)函數(shù)傳入ctl之后,是通過(guò)CTL&CAPACITY操作來(lái)獲取當(dāng)前運(yùn)行線程總數(shù)的。 也就是RunningState|WorkCount&CAPACITY,算出來(lái)的就是低28位的值。因?yàn)镃APACITY得到的就是高3位(29-31位)位0,低28位(0-28位)都是1,所以得到的就是ctl中低28位的值。 而runStateOf這個(gè)方法的話,算的就是RunningState|WorkCount&CAPACITY,高3位的值,因?yàn)镃APACITY是CAPACITY的取反,所以得到的就是高3位(29-31位)為1,低28位(0-28位)為0,所以通過(guò)&運(yùn)算后,所得到的值就是高3為的值。 簡(jiǎn)單來(lái)說(shuō)就是ctl中是高3位作為狀態(tài)值,低28位作為線程總數(shù)值來(lái)進(jìn)行存儲(chǔ)。

2.3.2 shutdownNow和shutdown的區(qū)別


看源碼發(fā)現(xiàn)有兩種近乎一樣的方法,shutdownNow和shutdown,設(shè)計(jì)者這么設(shè)計(jì)自然是有它的道理,那么這兩個(gè)方法的區(qū)別在哪呢?

  • shutdown會(huì)把線程池的狀態(tài)改為SHUTDOWN,而shutdownNow把當(dāng)前線程池狀態(tài)改為STOP。
  • shutdown只會(huì)中斷所有空閑的線程,而shutdownNow會(huì)中斷所有的線程。
  • shutdown返回方法為空,會(huì)將當(dāng)前任務(wù)隊(duì)列中的所有任務(wù)執(zhí)行完畢;而shutdownNow把任務(wù)隊(duì)列中的所有任務(wù)都取出來(lái)返回。


2.3.3 線程復(fù)用原理


    
final void runWorker(Worker w{
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask =  null;
        w.unlock();  // allow interrupts
        boolean completedAbruptly =  true;
         try {
             while (task !=  null || (task = getTask()) !=  null) {
                w. lock();
                 // If pool is stopping, ensure thread is interrupted;
                 // if not, ensure thread is not interrupted.  This
                 // requires a recheck in second case to deal with
                 // shutdownNow race while clearing interrupt
                 if ((runStateAtLeast(ctl. get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl. get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                 try {
                    beforeExecute(wt, task);
                    Throwable thrown =  null;
                     try {
                        task.run();
                    }  catch (RuntimeException x) {
                        thrown = x;  throw x;
                    }  catch (Error x) {
                        thrown = x;  throw x;
                    }  catch (Throwable x) {
                        thrown = x;  throw  new Error(x);
                    }  finally {
                        afterExecute(task, thrown);
                    }
                }  finally {
                    task =  null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly =  false;
        }  finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

就是任務(wù)在并不只執(zhí)行創(chuàng)建時(shí)指定的firstTask第一任務(wù),還會(huì)從任務(wù)隊(duì)列的中自己主動(dòng)取任務(wù)執(zhí)行,而且是有或者無(wú)時(shí)間限定的阻塞等待,以保證線程的存活。 默認(rèn)的是不允許。

2.4 CountDownLatch和CyclicBarrier區(qū)別


countDownLatch是一個(gè)計(jì)數(shù)器,線程完成一個(gè)記錄一個(gè),計(jì)數(shù)器遞減,只能只用一次。

CyclicBarrier的計(jì)數(shù)器更像一個(gè)閥門,需要所有線程都到達(dá),然后繼續(xù)執(zhí)行,計(jì)數(shù)器遞增,提供Reset功能,可以多次使用。


3. 多線程間通信的幾種方式


提及多線程又不得不提及多線程通信的機(jī)制。首先,要短信線程間通信的模型有兩種:共享內(nèi)存和消息傳遞,以下方式都是基本這兩種模型來(lái)實(shí)現(xiàn)的。我們來(lái)基本一道面試常見(jiàn)的題目來(lái)分析:

題目:有兩個(gè)線程A、B,A線程向一個(gè)集合里面依次添加元素"abc"字符串,一共添加十次,當(dāng)添加到第五次的時(shí)候,希望B線程能夠收到A線程的通知,然后B線程執(zhí)行相關(guān)的業(yè)務(wù)操作。

3.1使用volatile關(guān)鍵字


    
package thread;

/**
 * 
 * @author hxz
 * @description 多線程測(cè)試類
 * @version 1.0
 * @data 2020年2月15日 上午9:10:09
 */

public  class MyThreadTest {

     public static void main(String[] args) throws Exception {

        notifyThreadWithVolatile();

    }

     /**
     * 定義一個(gè)測(cè)試
     */

     private  static  volatile  boolean flag =  false;
     /**
     * 計(jì)算I++,當(dāng)I==5時(shí),通知線程B
     * @throws Exception
     */

     private static void notifyThreadWithVolatile() throws Exception {
        Thread thc =  new Thread( "線程A"){
             @Override
             public void run() {
                 for ( int i =  0; i <  10; i++) {
                     if (i ==  5) {
                        flag =  true;
                         try {
                            Thread.sleep( 500L);
                        }  catch (InterruptedException e) {
                             // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                         break;
                    }
                    System.out.println(Thread.currentThread().getName() +  "====" + i);
                }
            }
        };

        Thread thd =  new Thread( "線程B") {
             @Override
             public void run() {
                 while ( true) {
                     // 防止偽喚醒 所以使用了while
                     while (flag) {
                        System.out.println(Thread.currentThread().getName() +  "收到通知");
                        System.out.println( "do something");
                         try {
                            Thread.sleep( 500L);
                        }  catch (InterruptedException e) {
                             // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                         return ;
                    }

                }
            }
        };

        thd.start();
        Thread.sleep( 1000L);
        thc.start();

    }
}

個(gè)人認(rèn)為這是基本上最好的通信方式,因?yàn)锳發(fā)出通知B能夠立馬接受并Do Something。



免責(zé)聲明:本文內(nèi)容來(lái)源于網(wǎng)絡(luò),文章版權(quán)歸原作者所有,意在傳播相關(guān)技術(shù)知識(shí)&行業(yè)趨勢(shì),供大家學(xué)習(xí)交流,若涉及作品版權(quán)問(wèn)題,請(qǐng)聯(lián)系刪除或授權(quán)事宜。

End


關(guān)于華清遠(yuǎn)見(jiàn)

華清遠(yuǎn)見(jiàn)武漢中心位于武漢市洪山區(qū)武漢工程大學(xué)(武昌校區(qū))科技孵化器11 樓,學(xué)生的食宿與活動(dòng)十分便捷,實(shí)驗(yàn)設(shè)備、師資力量、教學(xué)管理等方面全國(guó)一流。武漢中心擁有專業(yè)級(jí)講師及資深工程師, 師資力量雄厚;擁有國(guó)內(nèi)最先進(jìn)的人工智能、云/大數(shù)據(jù)等開(kāi)發(fā)硬件專業(yè)實(shí)驗(yàn)設(shè)備,其中85%以上的實(shí)驗(yàn)設(shè)備由華清遠(yuǎn)見(jiàn)自主研發(fā);擁有以人為本的高級(jí)班主任老師,科學(xué)的教學(xué)管理制度。主要課程有嵌入式人工智能、Java大數(shù)據(jù)開(kāi)發(fā)、Html5全棧開(kāi)發(fā)、Python+人工智能等,并為每一位學(xué)員提供專業(yè)的就業(yè)指導(dǎo),高薪就業(yè)的學(xué)員不計(jì)其數(shù),做良心教育,做專業(yè)教育,做受人尊敬的職業(yè)教育。


 微信搜一搜 華清遠(yuǎn)見(jiàn)武漢中心 點(diǎn)分享 點(diǎn)點(diǎn)贊 點(diǎn)在看

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉