最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JAVA中關(guān)于多線程的使用詳解

 更新時(shí)間:2025年09月06日 14:14:04   作者:我今晚不熬夜  
本文給大家介紹JAVA中關(guān)于多線程的使用詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

多線程基礎(chǔ)

1  概述

現(xiàn)代操作系統(tǒng)(WindowsmacOS,Linux)都可以執(zhí)行多任務(wù)。多任務(wù)就是同時(shí)運(yùn)行多個(gè)任務(wù)。例如:播放音樂(lè)的同時(shí),瀏覽器可以進(jìn)行文件下載,同時(shí)可以進(jìn)行QQ消息的收發(fā)。

CPU執(zhí)行代碼都是一條一條順序執(zhí)行的,但是,即使是單核CPU,也可以同時(shí)運(yùn)行多個(gè)任務(wù)。因?yàn)椴僮飨到y(tǒng)執(zhí)行多任務(wù)實(shí)際上就是讓CPU對(duì)多個(gè)任務(wù)輪流交替執(zhí)行。

操作系統(tǒng)輪流讓多個(gè)任務(wù)交替執(zhí)行,例如,讓瀏覽器執(zhí)行0.001秒,讓QQ執(zhí)行0.001秒,再讓音樂(lè)播放器執(zhí)行0.001秒。在用戶使用的體驗(yàn)看來(lái),CPU就是在同時(shí)執(zhí)行多個(gè)任務(wù)。

1  進(jìn)程與線程

程序:程序是含有指令和數(shù)據(jù)的文件,被存儲(chǔ)在磁盤(pán)或其他的數(shù)據(jù)存儲(chǔ)設(shè)備中,可以理解為程序是包含靜態(tài)代碼的文件。例如:瀏覽器軟件、音樂(lè)播放器軟件等軟件的安裝目錄和文件。

進(jìn)程:進(jìn)程是程序的一次執(zhí)行過(guò)程,是系統(tǒng)運(yùn)行程序的基本單位。

線程:某些進(jìn)程內(nèi)部還需要同時(shí)執(zhí)行多個(gè)子任務(wù)。例如,我們?cè)谑褂?code>WPS時(shí),WPS可以讓我們一邊打字,一邊進(jìn)行拼寫(xiě)檢查,同時(shí)還可以在后臺(tái)進(jìn)行自動(dòng)保存和上傳云文檔,我們把子任務(wù)稱為線程。線程是進(jìn)程劃分成的更小的運(yùn)行單位。

進(jìn)程和線程的關(guān)系就是:一個(gè)進(jìn)程可以包含一個(gè)或多個(gè)線程,但至少會(huì)有一個(gè)主線程。

2  線程基本概念

單線程:?jiǎn)尉€程就是進(jìn)程中只有一個(gè)線程。單線程在程序執(zhí)行時(shí),所走的程序路徑按照連續(xù)順序排下來(lái),前面的必須處理好,后面的才會(huì)執(zhí)行

多線程:由一個(gè)以上的線程組成的程序稱為多線程程序。Java中,一定是從主線程開(kāi)始執(zhí)行(main方法),然后在主線程的某個(gè)位置創(chuàng)建并啟動(dòng)新的線程。

多線程的應(yīng)用場(chǎng)景

  • 軟件中的耗時(shí)操作,拷貝的遷移文件,加載大量資源的時(shí)候
  • 所有的后臺(tái)服務(wù)器
  • 所有的聊天軟件

3  線程的創(chuàng)建方式

3. 1 方式一:繼承java.lang.Thread類(lèi)(線程子類(lèi))

public class Demo01 {
    public static void main(String[] args) {
        //1.創(chuàng)建一個(gè)Thread的子類(lèi),重寫(xiě)run方法
        //2.創(chuàng)建子類(lèi)對(duì)象
        //3.調(diào)用start方法啟動(dòng)
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主線程執(zhí)行任務(wù):"+i);
        }
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+"正在執(zhí)行任務(wù):"+i);
        }
    }
}

3. 2 方式二:實(shí)現(xiàn)java.lang.Runnable接口(線程執(zhí)行類(lèi))

public class Demo02 {
    public static void main(String[] args) {
        //方式2:java.lang.Runnable  接口實(shí)現(xiàn)多線程
        //1.創(chuàng)建Runnable的實(shí)現(xiàn)類(lèi),重寫(xiě)run方法
        //2.創(chuàng)建Runnable的實(shí)現(xiàn)類(lèi)對(duì)象r1
        //3.創(chuàng)建Thread類(lèi)的對(duì)象,將r1作為構(gòu)造方法的參數(shù)進(jìn)行創(chuàng)建
        //4.調(diào)用start方法啟動(dòng)線程
        MyRun myRun = new MyRun();
        Thread t1 = new Thread(myRun);
        t1.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主線程執(zhí)行任務(wù):"+i);
        }
    }
}
class MyRun implements Runnable {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"線程執(zhí)行任務(wù):"+i);
        }
    }
}

3. 3 方式三:實(shí)現(xiàn)java.util.concurrent.Callable接口,允許子線程返回結(jié)果、拋出異常

public class Demo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //方式3:Callable接口方式實(shí)現(xiàn)多線程
        //步驟:
        //1.創(chuàng)建Callable的實(shí)現(xiàn)類(lèi),重寫(xiě)call方法
        //2.創(chuàng)建Callable的實(shí)現(xiàn)類(lèi)對(duì)象
        //3.創(chuàng)建FutureTask對(duì)象,用來(lái)進(jìn)行結(jié)果的管理操作
        //4.創(chuàng)建Thread類(lèi)的對(duì)象,將步驟3的對(duì)象作為參數(shù)傳遞
        //5.啟動(dòng)線程
        MyCallable callable1 = new MyCallable(1,10);
        FutureTask<Integer> futureTask1 = new FutureTask<Integer>(callable1);
        Thread thread1=new Thread(futureTask1);
        thread1.setName("線程1");
        thread1.start();
        MyCallable callable2 = new MyCallable(2,11);
        FutureTask<Integer> futureTask2 = new FutureTask<Integer>(callable2);
        Thread thread2=new Thread(futureTask2);
        thread2.setName("線程2");
        thread2.start();
        System.out.println("子線程C1和為:"+futureTask1.get());
        System.out.println("子線程C2和為:"+futureTask2.get());
        for (int i=1;i<=10;i++) {
            System.out.println("主線程:"+i);
        }
    }
}
class MyCallable implements Callable<Integer> {
    int begin,end;
    public MyCallable(int begin,int end) {
        this.begin = begin;
        this.end = end;
    }
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = begin; i <= end; i++) {
            sum += i;
            System.out.println(Thread.currentThread().getName()+"正在進(jìn)行加"+i+"操作");
        }
        return sum;
    }
}

3. 4 方式四:線程池

public class Demo04 {
    public static void main(String[] args) {
        //使用線程池創(chuàng)建線程對(duì)象
        ExecutorService ex= Executors.newCachedThreadPool();
        ex.execute(new MyRun());
        ex.execute(new MyRun());
    }
}

總結(jié):

  • Thread   編程比較簡(jiǎn)單,可以直接使用Thread類(lèi)中的方法  可擴(kuò)展性差
  • Runnable  拓展性強(qiáng),實(shí)現(xiàn)該接口后還可以繼承其他的類(lèi)再實(shí)現(xiàn)其他的接口
  • Callable   拓展性強(qiáng),實(shí)現(xiàn)該接口還可以繼承其他類(lèi),可以有返回值

4  線程所擁有的方法

4.1  getName()獲取線程名

public class Demo01 {
    public static void main(String[] args) {
        //獲取當(dāng)前線程
        Thread t1=Thread.currentThread();
        System.out.println("線程對(duì)象: "+t1);
        //獲取線程名  getName(),如果沒(méi)有為線程命名,系統(tǒng)會(huì)默認(rèn)指定線程名,命名規(guī)則是Thread-N的形式
        System.out.println("線程名:"+t1.getName());
    }
}

4.2  setName()設(shè)置線程名

public class Demo02 {
    public static void main(String[] args) {
        //給線程設(shè)置名字,setName()
        //讓當(dāng)前線程休眠:Thread.sleep(long ,min)  參數(shù)為毫秒值
        MyThread t1 = new MyThread();
        t1.setName("土豆");
        t1.start();
        //使用構(gòu)造方法設(shè)置線程名
        MyThread t2 = new MyThread("洋芋");
        t2.start();
        //Runnable作為參數(shù)
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"線程正在啟動(dòng)");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"線程運(yùn)行結(jié)束");
            }
        };
        Thread thread = new Thread(runnable);
        thread.setName("馬鈴薯");
        thread.start();
    }
}

4.3  sleep()線程休眠

public class MyThread extends Thread {
    public MyThread(){}
    public MyThread(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println("當(dāng)前線程為:"+getName());
        for(int i=1; i<=5; i++){
            try {
                //休眠1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(i);
            }
        }
    }
}

4.4  setPriority(long newPriority)  設(shè)置線程優(yōu)先級(jí)

public class Demo03 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            public void run() {
                for (char i = 'a'; i <= 'g'; i++) {
                    System.out.println(Thread.currentThread().getName() + ": " + i);
                }
            }
        };
        Runnable runnable2 = new Runnable() {
            public void run() {
                for (char i = 'A'; i <= 'J'; i++) {
                    System.out.println(Thread.currentThread().getName() + ": " + i);
                }
            }
        };
        Thread t1=new Thread(runnable,"線程1");
        Thread t2=new Thread(runnable2,"線程2");
        //設(shè)置線程優(yōu)先級(jí)  1~10,
        //注意:優(yōu)先級(jí)高的線程搶占到資源的概率較大,但不一定優(yōu)先搶占到資源
        t1.setPriority(1);
        t2.setPriority(10);
        t1.start();
        t2.start();
        //獲取線程優(yōu)先級(jí)
        System.out.println(t1.getName()+"的優(yōu)先級(jí)為"+t1.getPriority());
        System.out.println(t2.getName()+"的優(yōu)先級(jí)為"+t2.getPriority());
        System.out.println(Thread.currentThread().getName()+"的優(yōu)先級(jí)為"+Thread.currentThread().getPriority());
    }
}

4.5  setDaemon設(shè)置守護(hù)線程

public class Demo04 {
    public static void main(String[] args) {
        Thread t1 = new Thread("線程1") {
            public void run() {
                for (char i = 'a'; i < 'z'; i++) {
                    System.out.println(getName()+":"+i);
                }
            }
        };
        Thread t2 = new Thread("線程2") {
            public void run() {
                for (char i = 'A'; i < 'Z'; i++) {
                    System.out.println(getName()+":"+i);
                }
            }
        };
        //t2線程設(shè)置為守護(hù)線程
        //細(xì)節(jié):  當(dāng)其他的非守護(hù)線程執(zhí)行完畢后,守護(hù)線程會(huì)陸陸續(xù)續(xù)的執(zhí)行結(jié)束
        t2.setDaemon(true);
        t1.start();
        t2.start();
    }
}

4.6  yield()線程禮讓

public class Demo05 {
    public static void main(String[] args) {
        Thread t1 = new Thread("線程1") {
            public void run() {
                for (char i = 'a'; i < 'z'; i++) {
                    System.out.println(getName()+":"+i);
                    Thread.yield();
                }
            }
        };
        Thread t2 = new Thread("線程2") {
            public void run() {
                Thread.yield();
                for (char i = 'A'; i < 'Z'; i++) {
                    System.out.println(getName()+":"+i);
                }
            }
        };
        t1.start();
        t2.start();
    }
}

4.7  join()線程插隊(duì)

public class Demo06 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(){
            public void run(){
                System.out.println("進(jìn)到"+getName()+"之中");
                for(int i=1;i<=100;i++){
                    System.out.println(i);
                }
                System.out.println("結(jié)束");
            }
        };
        t1.start();
        t1.join();  //線程的插隊(duì),插入當(dāng)前的線程的前面
        //主線程執(zhí)行的任務(wù)
        for(char i='A';i<='z';i++){
            System.out.println("main:"+i);
        }
    }
}

 4.8  interrupt()線程的中斷

public class Demo07 {
    public static void main(String[] args) {
        Thread t1 = new Thread("線程1"){
            public void run(){
                //獲取當(dāng)前系統(tǒng)時(shí)間
                long startTime = System.currentTimeMillis();
                System.out.println("進(jìn)入到"+getName()+"線程中");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    System.out.println("中斷"+getName()+"線程");
                    e.printStackTrace();
                }
                System.out.println("結(jié)束"+getName()+"線程");
                long endTime = System.currentTimeMillis();
                System.out.println(getName()+"運(yùn)行時(shí)間:"+(endTime-startTime));
            }
        };
        t1.start();
        //讓主線程休眠
        System.out.println("main線程進(jìn)入");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //main主線程修改t1線程的中斷狀態(tài)=true
        //t1線程檢測(cè)中斷狀態(tài)=true,則拋出InterruptedException,子線程執(zhí)行結(jié)束
        t1.interrupt();
    }
}

5  多線程同步

Synchronized同步鎖,簡(jiǎn)單來(lái)說(shuō),使用Synchronized關(guān)鍵字將一段代碼邏輯,用一把鎖給鎖起來(lái),只有獲得了這把鎖的線程才訪問(wèn)。并且同一時(shí)刻, 只有一個(gè)線程能持有這把鎖, 這樣就保證了同一時(shí)刻只有一個(gè)線程能執(zhí)行被鎖住的代碼,從而確保代碼的線程安全。

看不懂?沒(méi)有關(guān)系,我也看不懂,簡(jiǎn)單來(lái)說(shuō)就是,如果有多個(gè)線程訪問(wèn)同一個(gè)資源,如果在不加鎖的情況下,資源會(huì)混亂,比如有三個(gè)線程同時(shí)訪問(wèn)一個(gè)數(shù)number,并且修改他的值,那么這個(gè)值最終大概會(huì)達(dá)不到想要的結(jié)果,產(chǎn)生數(shù)據(jù)混亂,比如兩個(gè)線程分別對(duì)一個(gè)數(shù)進(jìn)行操作,其中一個(gè)加100次1,另外一個(gè)減100次1,預(yù)想的結(jié)果應(yīng)該還是這個(gè)數(shù)本身,但如果不加鎖,那么他極有可能變?yōu)閯e的值。

synchronized 關(guān)鍵字的用法

* 1.加鎖:
*  鎖對(duì)象可以是任意類(lèi)型的對(duì)象,必須要保證多條線程共用一個(gè)鎖對(duì)象
*      synchronized(鎖對(duì)象){
*          操作的共享代碼
*      }
*
* 某條線程獲取到了鎖資源,鎖關(guān)閉,當(dāng)里面的任務(wù)執(zhí)行完成,鎖釋放
* 默認(rèn)情況下,鎖是打開(kāi)狀態(tài)
*
* 2.鎖方法:
*    synchronized加到方法上,變成了鎖方法,注意鎖不能自己指定
*    修飾符  synchronized  返回值類(lèi)型  方法名(){
*        操作的共享代碼
*    }
*  鎖:同步鎖不能自己指定,普通方法鎖是當(dāng)前對(duì)象

1  修飾實(shí)例方法:

當(dāng)使用synchronized 修飾實(shí)例方法時(shí), 以下兩種寫(xiě)法作用和意義相同:

方法1:

public class ShouMai extends Thread {
    public static int ticket = 1000;
    public static Object lock = new Object();
    public ShouMai(String name) {
        super(name);
    }
    //請(qǐng)加鎖,保證線程安全
    @Override
    public void run() {
        while (true) {
            synchronized (ShouMai.class) {
                if (ticket > 0) {
                    System.out.println(getName() + "正在售賣(mài)第" + (1000 - --ticket) + "張票");
                } else {
                    System.out.println(getName() + ":票已售罄");
                    break;
                }
            }
        }
    }
}

方式2:

public class ShouMai1 extends Thread {
    public static int ticket = 1000;
    public ShouMai1(String name) {
        super(name);
    }
    //加鎖,保證線程安全
    @Override
    public void run() {
        while (true) {
            if(!shoumai()) break;
        }
    }
    public synchronized static boolean shoumai() {
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "正在售賣(mài)第" + (1000 - --ticket) + "張票");
            return true;
        } else {
            System.out.println(Thread.currentThread().getName() + ":票已售罄");
            return false;
        }
    }
}

2  修飾靜態(tài)方法

public class ShouMai2Imp implements Runnable {
    public static int ticket=1000;
    @Override
    public void run() {
        while (true){
            if(!shoumai()) break;
        }
    }
    public synchronized static boolean shoumai(){
        if (ticket<=0) return false;
        System.out.println(Thread.currentThread().getName() + "正在售賣(mài)第" + (1000 - --ticket) + "張票");
        return true;
    }
}

3  修飾代碼塊

synchronized(自定義對(duì)象) {
   //臨界區(qū)
}

lock鎖的使用

在java中,可以是使用Object.lock()方法進(jìn)行加鎖,Object.unlock()方法進(jìn)行解鎖

public class ShouMai3 extends Thread {
    public static int ticket = 200;
    private static Lock lock = new ReentrantLock();
    public ShouMai3(String name) {
        super(name);
    }
    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (ticket > 0) {
                    System.out.println(getName() + "正在售賣(mài)第" + (200 - --ticket) + "張票");
                } else {
                    System.out.println(getName() + ":票已售罄");
                    break;
                }
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

6  線程的狀態(tài)

在整個(gè)線程的生命周期中,線程的狀態(tài)有以下6種:

  • New:新建狀態(tài),新創(chuàng)建的線程,此時(shí)尚未調(diào)用start()方法;
  • Runnable:運(yùn)行狀態(tài),運(yùn)行中的線程,已經(jīng)調(diào)用start()方法,線程正在或即將執(zhí)行run()方法;
  • Blocked:阻塞狀態(tài),運(yùn)行中的線程,在等待競(jìng)爭(zhēng)鎖時(shí),被阻塞,暫不執(zhí)行;
  • Waiting:等待狀態(tài),運(yùn)行中的線程,因?yàn)?code>join()等方法調(diào)用,進(jìn)入等待;
  • Timed Waiting:計(jì)時(shí)等待狀態(tài),運(yùn)行中的線程,因?yàn)閳?zhí)行sleep(等待毫秒值)join(等待毫秒值)等方法,進(jìn)入計(jì)時(shí)等待;
  • Terminated:終止?fàn)顟B(tài),線程已終止,因?yàn)?code>run()方法執(zhí)行完畢。

當(dāng)線程啟動(dòng)后,它可以在RunnableBlocked、WaitingTimed Waiting這幾個(gè)狀態(tài)之間切換,直到最后變成Terminated狀態(tài),線程終止

1  NEW

新建狀態(tài),新創(chuàng)建的線程,此時(shí)尚未調(diào)用start()方法

public class Demo01 {
    public static void main(String[] args) {
        Thread thread=new Thread();
        System.out.println(thread.getState());
    }
}

2  RUNNABLE

運(yùn)行狀態(tài),運(yùn)行中的線程,已經(jīng)調(diào)用start()方法,線程正在或即將執(zhí)行run方法

public class Demo02 {
    public static void main(String[] args) {
        Thread t1=new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("正在執(zhí)行任務(wù)");
                }
            }
        };
        //啟動(dòng)線程
        t1.start();
        System.out.println(t1.getState());
    }
}

3  Terminated

終止?fàn)顟B(tài),線程已終止,因?yàn)閞un()方法執(zhí)行完畢

public class Demo03 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("正在執(zhí)行任務(wù)");
            }
        });
        //啟動(dòng)線程
        thread.start();
        //等待子線程完成任務(wù)
        Thread.sleep(500);
        //獲取thread線程狀態(tài)
        System.out.println(thread.getState());
    }
}

4  BLOCKED與TIMED_WAITING

  • BLOCKED  阻塞狀態(tài),運(yùn)行中的線程,在等待競(jìng)爭(zhēng)鎖時(shí),被阻塞,暫不執(zhí)行
  • TIMED_WAITING  計(jì)時(shí)等待狀態(tài),運(yùn)行中的線程,因?yàn)閳?zhí)行sleep(等待毫秒值)

     sleep()  不釋放鎖資源
     wait()   釋放鎖資源  --  Object中的方法,wait()調(diào)用時(shí)對(duì)象必須要和鎖對(duì)象一樣

public class Demo04 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    //如果某條線程獲取到鎖資源,任務(wù)一直執(zhí)行,不釋放鎖資源
                    while (true) {
                        //死循環(huán)
                        try {
                            Thread.sleep(1000);
//                            lock.wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        break;
                    }
                }
            }
        };
        Thread thread1 = new Thread(runnable,"線程1");
        Thread thread2 = new Thread(runnable,"線程2");
        thread1.start();
        thread2.start();
        //讓主線程休眠500毫秒,讓t1和t2同時(shí)競(jìng)爭(zhēng)一個(gè)鎖資源
        Thread.sleep(500);
        System.out.println(thread1.getName()+":"+thread1.getState());
        System.out.println(thread2.getName()+":"+thread2.getState());
    }
}

5  WAITING

等待狀態(tài),運(yùn)行中的線程,因?yàn)?code>join()等方法調(diào)用,進(jìn)入等待;

public class Demo05 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(500);
        System.out.println(thread.getState());
    }
}
public class Demo06 {
    public static void main(String[] args) throws InterruptedException {
        Thread outThread = new Thread(new Runnable() {
            public void run() {
                System.out.println("外部線程啟動(dòng)");
                //在內(nèi)部又創(chuàng)建了一個(gè)線程
                Thread innerThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("內(nèi)部線程啟動(dòng)");
                        //innerThread任務(wù)一直執(zhí)行
                        while (true){
                            try {
                                Thread.sleep(2000);
                                break;
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                });
                innerThread.start();
                try {
                    innerThread.join();  //innerThread插隊(duì),插隊(duì)插到outThread前面,outThread此時(shí)處于等待innerThread完成任務(wù)狀態(tài),所以outThread等待
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        outThread.start();  //線程啟動(dòng)
        //讓主線程休眠,目的時(shí)為了讓outThread運(yùn)行起來(lái)
        Thread.sleep(100);
        //獲取outThread的狀態(tài)
        System.out.println(outThread.getState());
    }
}

7  線程池

概念

線程池內(nèi)部維護(hù)了若干個(gè)線程,沒(méi)有任務(wù)的時(shí)候,這些線程都處于等待空閑狀態(tài)。如果有新的線程任務(wù),就分配一個(gè)空閑線程執(zhí)行。如果所有線程都處于忙碌狀態(tài),線程池會(huì)創(chuàng)建一個(gè)新線程進(jìn)行處理或者放入隊(duì)列(工作隊(duì)列)中等待。

┌─────┐ execute  ┌──────────────────┐
│Task1│─────────>│ThreadPool        │
├─────┤          │┌───────┐┌───────┐│
│Task2│          ││Thread1││Thread2││
├─────┤          │└───────┘└───────┘│
│Task3│          │┌───────┐┌───────┐│
├─────┤          ││Thread3││Thread4││
│Task4│          │└───────┘└───────┘│
├─────┤          └──────────────────┘
│Task5│
├─────┤
│Task6│
└─────┘
  ...

線程池常用方法

  • 執(zhí)行無(wú)返回值的線程任務(wù)void execute(Runnable command);
  • 提交有返回值的線程任務(wù)Future<T> submit(Callable<T> task);
  • 關(guān)閉線程池void shutdown();shutdownNow();
  • 等待線程池關(guān)閉boolean awaitTermination(long timeout, TimeUnit unit);

執(zhí)行線程任務(wù)

execute()只能提交Runnable類(lèi)型的任務(wù),沒(méi)有返回值,而submit()既能提交Runnable類(lèi)型任務(wù)也能提交Callable類(lèi)型任務(wù),可以返回Future類(lèi)型結(jié)果,用于獲取線程任務(wù)執(zhí)行結(jié)果。

execute()方法提交的任務(wù)異常是直接拋出的,而submit()方法是捕獲異常,當(dāng)調(diào)用Futureget()方法獲取返回值時(shí),才會(huì)拋出異常。

線程池分類(lèi)

Java標(biāo)準(zhǔn)庫(kù)提供的幾種常用線程池,創(chuàng)建這些線程池的方法都被封裝到Executors工具類(lèi)中。

  • FixedThreadPool線程數(shù)固定的線程池,使用Executors.newFixedThreadPool()創(chuàng)建;
  • CachedThreadPool線程數(shù)根據(jù)任務(wù)動(dòng)態(tài)調(diào)整的線程池,使用Executors.newCachedThreadPool()創(chuàng)建;
  • SingleThreadExecutor:僅提供一個(gè)單線程的線程池,使用Executors.newSingleThreadExecutor()創(chuàng)建;
  • ScheduledThreadPool:能實(shí)現(xiàn)定時(shí)、周期性任務(wù)的線程池,使用Executors.newScheduledThreadPool()創(chuàng)建;

FixedThreadPool

public class Demo01 {
    public static void main(String[] args) throws InterruptedException {
        //1.獲取線程池對(duì)象
        ExecutorService executorService= Executors.newFixedThreadPool(3);
        //2.提交任務(wù)給線程池對(duì)象
        executorService.execute(new MyRunnable("任務(wù)1"));
        executorService.execute(new MyRunnable("任務(wù)2"));
        executorService.execute(new MyRunnable("任務(wù)3"));
        Thread.sleep(100);  //主線程休眠
        executorService.execute(new MyRunnable("任務(wù)4"));
        //3.線程池的關(guān)閉方法  線程池在程序結(jié)束的時(shí)候要關(guān)閉。使用shutdown()方法關(guān)閉線程池的時(shí)候,它會(huì)等待正在執(zhí)行的任務(wù)先完成,然后再關(guān)閉。shutdownNow()會(huì)立刻停止正在執(zhí)行的任務(wù);
        executorService.shutdown();
//        executorService.shutdownNow();
        /*
        當(dāng)使用awaitTermination()方法時(shí),主線程會(huì)處于一種等待的狀態(tài),按照指定的timeout檢查線程池。
        第一個(gè)參數(shù)指定的是時(shí)間,第二個(gè)參數(shù)指定的是時(shí)間單位(當(dāng)前是秒)。返回值類(lèi)型為boolean型。
        如果等待的時(shí)間超過(guò)指定的時(shí)間,但是線程池中的線程運(yùn)行完畢,awaitTermination()返回true。
        如果等待的時(shí)間超過(guò)指定的時(shí)間,但是線程池中的線程未運(yùn)行完畢,awaitTermination()返回false。
        如果等待時(shí)間沒(méi)有超過(guò)指定時(shí)間,則繼續(xù)等待。
         */
        while (!executorService.awaitTermination(1, TimeUnit.SECONDS)){
            System.out.println("還沒(méi)關(guān)閉線程池");
        };
        System.out.println("已經(jīng)關(guān)閉線程池");
    }
}
class MyRunnable implements Runnable{
    public String str;
    public MyRunnable(String str) {
        this.str = str;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"  開(kāi)始執(zhí)行"+str);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
//        for (int i = 0; i < 10; i++) {
//            System.out.println(Thread.currentThread().getName()+":"+i);
//        }
        System.out.println(Thread.currentThread().getName()+"  結(jié)束"+str);
    }
}

CachedThreadPool

public class Demo02 {
    public static void main(String[] args) throws InterruptedException {
        //創(chuàng)建一個(gè)線程數(shù)量無(wú)上限的線程池對(duì)象
        ExecutorService executorService= Executors.newCachedThreadPool();
        for (int i = 1; i <= 100; i++) {
            executorService.execute(new MyRunnable("任務(wù)"+i));
        }
        Thread.sleep(2000);
        executorService.execute(new MyRunnable("最后一個(gè)任務(wù)"));
        executorService.shutdown();
    }
}

SingleThreadExecutor

public class Demo03 {
    public static void main(String[] args) {
        ExecutorService executorService= Executors.newSingleThreadExecutor();
        //循環(huán)的提交任務(wù)
        for (int i = 1; i < 10; i++) {
            executorService.execute(new MyRunnable("任務(wù)"+i));
        }
        executorService.shutdown();
    }
}

ScheduledThreadPool

public class Demo04 {
    public static void main(String[] args) {
        ScheduledExecutorService executorService= Executors.newScheduledThreadPool(2);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"開(kāi)始執(zhí)行");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()+"執(zhí)行結(jié)束");
            }
        };
        //2.提交任務(wù)  --延時(shí)多久進(jìn)行任務(wù)的執(zhí)行
        //schedule(Runnable command,long delay, TimeUnit unit)  任務(wù)對(duì)象  延遲時(shí)間  時(shí)間單位
//        executorService.schedule(runnable,2,TimeUnit.SECONDS);
        //scheduleAtFixedRate  延時(shí)1秒首次執(zhí)行此任務(wù),每隔2秒進(jìn)行此任務(wù)的執(zhí)行
        executorService.scheduleAtFixedRate(runnable, 1, 2, TimeUnit.SECONDS);
        //上一次任務(wù)執(zhí)行完畢后,等待固定的時(shí)間間隔,再進(jìn)行下一次任務(wù)
//        executorService.scheduleWithFixedDelay(runnable, 2, 3, TimeUnit.SECONDS);
    }
}

自定義線程池

在《阿里巴巴java開(kāi)發(fā)手冊(cè)》中指出了線程資源必須通過(guò)線程池提供,不允許在應(yīng)用中自行顯示的創(chuàng)建線程,這樣一方面是線程的創(chuàng)建更加規(guī)范,可以合理控制開(kāi)辟線程的數(shù)量;另一方面線程的細(xì)節(jié)管理交給線程池處理,優(yōu)化了資源的開(kāi)銷(xiāo)。而線程池不允許使用Executors去創(chuàng)建,而要通過(guò)ThreadPoolExecutor方式。jdkExecutor框架雖然提供了如newFixedThreadPool()、newSingleThreadExecutor()newCachedThreadPool()等創(chuàng)建線程池的方法,但都有其局限性,不夠靈活;另外由于前面幾種方法內(nèi)部也是通過(guò)new ThreadPoolExecutor方式實(shí)現(xiàn),使用ThreadPoolExecutor有助于大家明確線程池的運(yùn)行規(guī)則,創(chuàng)建符合自己的業(yè)務(wù)場(chǎng)景需要的線程池,避免資源耗盡的風(fēng)險(xiǎn)。

必須為線程池中的線程,按照業(yè)務(wù)規(guī)則,進(jìn)行命名。可以在創(chuàng)建線程池時(shí),使用自定義線程工廠規(guī)范線程命名方式,避免線程使用默認(rèn)名稱。

如何創(chuàng)建一個(gè)線程池?

public ThreadPoolExecutor(int corePoolSize,  核心線程數(shù) >=0
                    int maximumPoolSize,   最大線程數(shù)(核心+臨時(shí)) >corePoolSize
                    long keepAliveTime,    臨時(shí)線程存活時(shí)間
                    TimeUnit unit,          存活的時(shí)間單位
                    BlockingQueue<Runnable> workQueue,    等待隊(duì)列
                    ThreadFactory threadFactory,  線程工廠
                    RejectedExecutionHandler handler)   拒絕策略
拒絕策略
  ThreadPoolExecutor.AbortPolicy()   默認(rèn)拒絕策略--拋異常
  ThreadPoolExecutor.DiscardOldestPolicy()  丟棄等待時(shí)間最久的任務(wù)
  ThreadPoolExecutor.DiscardPolicy()  丟棄當(dāng)前的任務(wù)
  ThreadPoolExecutor.CallerRunsPolicy()  讓當(dāng)前的線程進(jìn)行任務(wù)的執(zhí)行
ExecutorService executorService = new ThreadPoolExecutor(3, 5, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
        //提交任務(wù)
executorService.submit(new MyRunnable("任務(wù)1"));  //提交一個(gè)任務(wù)給executorService對(duì)象
executorService.submit(new MyRunnable("任務(wù)2"));
executorService.submit(new MyRunnable("任務(wù)3"));
executorService.submit(new MyRunnable("任務(wù)4"));  //核心線程已滿,進(jìn)入等待隊(duì)列
executorService.submit(new MyRunnable("任務(wù)5"));
executorService.submit(new MyRunnable("任務(wù)6"));
executorService.submit(new MyRunnable("任務(wù)7"));   //核心線程已滿,等待隊(duì)列已滿,進(jìn)入臨時(shí)線程
executorService.submit(new MyRunnable("任務(wù)8"));
executorService.submit(new MyRunnable("任務(wù)9"));   //核心線程已滿,等待隊(duì)列已滿,臨時(shí)線程已滿,拋出異常
executorService.shutdown();

自定義線程工廠與拒絕策略

自定義線程工廠
class MyThreadFactory implements ThreadFactory {
    //線程池名稱前綴
    String namePrefix;
    public MyThreadFactory(String namePrefix) {
        this.namePrefix = namePrefix;
    }
    //具備原子性的Integer類(lèi)型
    private AtomicInteger threadNumber = new AtomicInteger(1);
    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r,namePrefix+"線程"+threadNumber.getAndIncrement());
    }
}

主要實(shí)現(xiàn)ThreadFactory接口中的newThread方法,我寫(xiě)的這個(gè)方法也就是對(duì)線程池與線程池中的線程進(jìn)行了重命名

自定義拒絕策略
class MyRejectedExecutionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("哎呦,達(dá)到能執(zhí)行任務(wù)的上限了,(寶?寶)做不到啊");
    }
}

在這里呢我自定義了一個(gè)拒絕策略,當(dāng)有一個(gè)線程進(jìn)入到線程池后,核心線程,線程隊(duì)列,臨時(shí)線程等都沒(méi)有空閑位置時(shí),會(huì)直接輸出一段話,線程不會(huì)進(jìn)入線程池之中。

線程池的狀態(tài)

線程池的狀態(tài)分為:RUNNING , SHUTDOWN , STOP , TIDYING , TERMINATED

RUNNING:運(yùn)行狀態(tài),線程池被一旦被創(chuàng)建,就處于RUNNING狀態(tài),并且線程池中的任務(wù)數(shù)為0。該狀態(tài)的線程池會(huì)接收新任務(wù),并處理工作隊(duì)列中的任務(wù)。

  •         調(diào)用線程池的shutdown()方法,可以切換到SHUTDOWN關(guān)閉狀態(tài);
  •         調(diào)用線程池的shutdownNow()方法,可以切換到STOP停止?fàn)顟B(tài);

SHUTDOWN :關(guān)閉狀態(tài),該狀態(tài)的線程池不會(huì)接收新任務(wù),但會(huì)處理工作隊(duì)列中的任務(wù);當(dāng)工作隊(duì)列為空時(shí),并且線程池中執(zhí)行的任務(wù)也為空時(shí),線程池進(jìn)入TIDYING狀態(tài);

STOP:停止?fàn)顟B(tài),該狀態(tài)的線程不會(huì)接收新任務(wù),也不會(huì)處理阻塞隊(duì)列中的任務(wù),而且會(huì)中斷正在運(yùn)行 的任務(wù);線程池中執(zhí)行的任務(wù)為空,進(jìn)入TIDYING狀態(tài);

TIDYING :整理狀態(tài),該狀態(tài)表明所有的任務(wù)已經(jīng)運(yùn)行終止,記錄的任務(wù)數(shù)量為0terminated()執(zhí)行完畢,進(jìn)入TERMINATED狀態(tài);

TERMINATED : 終止?fàn)顟B(tài),該狀態(tài)表示線程池徹底關(guān)閉。

線程池的優(yōu)點(diǎn)

  1. 提高響應(yīng)的速度
  2. 提高線程的可管理性
  3. 降低資源消耗(線程執(zhí)行完任務(wù),不銷(xiāo)毀,可以執(zhí)行其他的任務(wù))

線程池核心代碼

線程池相關(guān)的接口和實(shí)現(xiàn)類(lèi)

線程池技術(shù),是由2個(gè)核心接口和一組實(shí)現(xiàn)類(lèi)組成。

Executor接口作為線程池技術(shù)中的頂層接口,它的作用是用來(lái)定義線程池中,用于提交并執(zhí)行線程任務(wù)的核心方法:exuecte()方法。未來(lái)線程池中所有的線程任務(wù),都將由exuecte()方法來(lái)執(zhí)行。

ExecutorService接口繼承了Executor接口,擴(kuò)展了awaitTermination()、submit()、shutdown()等專門(mén)用于管理線程任務(wù)的方法。

ExecutorService接口的抽象實(shí)現(xiàn)類(lèi)AbstractExecutorService,為不同的線程池實(shí)現(xiàn)類(lèi),提供submit()、invokeAll()等部分方法的公共實(shí)現(xiàn)。但是由于在不同線程池中的核心方法exuecte()執(zhí)行策略不同,所以在AbstractExecutorService并未提供該方法的具體實(shí)現(xiàn)。

AbstractExecutorService有兩個(gè)常見(jiàn)的子類(lèi)ThreadPoolExecutorForkJoinPool,用于實(shí)現(xiàn)不同的線程池。

ThreadPoolExecutor線程池通過(guò)Woker工作線程、BlockingQueue阻塞工作隊(duì)列 以及 拒絕策略實(shí)現(xiàn)了一個(gè)標(biāo)準(zhǔn)的線程池;

ForkJoinPool是一個(gè)基于分治思想的線程池實(shí)現(xiàn)類(lèi),通過(guò)分叉(fork)合并(join)的方式,將一個(gè)大任務(wù)拆分成多個(gè)小任務(wù),并且為每個(gè)工作線程提供一個(gè)工作隊(duì)列,減少競(jìng)爭(zhēng),實(shí)現(xiàn)并行的線程任務(wù)執(zhí)行方式,所以ForkJoinPool適合計(jì)算密集型場(chǎng)景,是ThreadPoolExecutor線程池的一種補(bǔ)充。

ScheduledThreadPoolExecutor類(lèi)是ThreadPoolExecutor類(lèi)的子類(lèi),按照時(shí)間周期執(zhí)行線程任務(wù)的線程池實(shí)現(xiàn)類(lèi),通常用于作業(yè)調(diào)度相關(guān)的業(yè)務(wù)場(chǎng)景。由于該線程池的工作隊(duì)列使用DelayedWorkQueue,這是一個(gè)按照任務(wù)執(zhí)行時(shí)間進(jìn)行排序的優(yōu)先級(jí)工作隊(duì)列,所以這也是ScheduledThreadPoolExecutor線程池能按照時(shí)間周期來(lái)執(zhí)行線程任務(wù)的主要原因。

工作線程Worker類(lèi)

每個(gè)Woker類(lèi)的對(duì)象,都代表線程池中的一個(gè)工作線程。

當(dāng)ThreadPoolExecutor線程池,通過(guò)exeute()方法執(zhí)行1個(gè)線程任務(wù)時(shí),會(huì)調(diào)用addWorker()方法創(chuàng)建一個(gè)Woker工作線程對(duì)象。并且,創(chuàng)建好的Worker工作線程對(duì)象,會(huì)被添加到一個(gè)HashSet<Worker> workders工作線程集合,統(tǒng)一由線程池進(jìn)行管理。

通過(guò)閱讀源代碼,可以看出Worker類(lèi)是ThreadPoolExecutor類(lèi)中定義的一個(gè)私有內(nèi)部類(lèi),保存了每個(gè)Worker工作線程要執(zhí)行的Runnable線程任務(wù)和Thread線程對(duì)象。

當(dāng)創(chuàng)建Worker工作線程時(shí),會(huì)通過(guò)構(gòu)造方法保存Runnable線程任務(wù),同時(shí)使用ThreadFactory線程工廠,為該工作線程創(chuàng)建一個(gè)Thread線程對(duì)象。通過(guò)這樣的操作,每個(gè)Worker工作線程對(duì)象,都將綁定一個(gè)真正的Thread線程。

另外,當(dāng)Thread線程被JVM調(diào)度執(zhí)行時(shí),線程將會(huì)自動(dòng)執(zhí)行Worker工作線程對(duì)象的run()方法,通過(guò)調(diào)用runWorker()方法,最終實(shí)現(xiàn)Woker工作線程中所保存的Runnable線程任務(wù)的執(zhí)行。

值得重視的是:當(dāng)Worker工作線程,在第一次執(zhí)行完成線程任務(wù)后,這個(gè)Worker工作線程并不會(huì)銷(xiāo)毀,而是會(huì)以循環(huán)的方式,通過(guò)線程池的getTask()方法,獲取阻塞工作隊(duì)列中新的Runnable線程任務(wù),并通過(guò)當(dāng)前Worker工作線程中所綁定Thread線程,完成新線程任務(wù)的執(zhí)行,從而實(shí)現(xiàn)了線程池的中Thread線程的重復(fù)使用。

核心方法:execute()方法

ThreadPoolExecutor線程池中,會(huì)通過(guò)execute(Runnable command)方法執(zhí)行Runnable類(lèi)型的線程任務(wù)。

完整實(shí)現(xiàn)了Executor接口定義execute()方法,這個(gè)方法作用是執(zhí)行一個(gè)Runnable類(lèi)型的線程任務(wù)。整體的執(zhí)行流程是:

  • 首先,通過(guò)AtomicInteger類(lèi)型的ctl對(duì)象,獲取線程池的狀態(tài)和工作線程數(shù);
  • 然后,判斷當(dāng)前線程池中的工作線程數(shù);
  • 如果,工作線程的數(shù)量小于核心線程數(shù),則通過(guò)addWorker()方法,創(chuàng)建新的Worker工作線程,并添加至workers工作線程集合;
  • 如果,工作線程的數(shù)量大于核心線程數(shù),并且線程池處于RUNNING狀態(tài),那么,線程池會(huì)將Runnable類(lèi)型的線程任務(wù),緩存至workQueue阻塞工作隊(duì)列,等待某個(gè)空閑工作線程獲取并執(zhí)行該任務(wù);
  • 如果,workQueue工作隊(duì)列緩存線程任務(wù)失敗,代表工作隊(duì)列已滿。那么,線程池會(huì)重新通過(guò)addWorker()方法,嘗試創(chuàng)建新的工作線程;
  • 這次創(chuàng)建時(shí),會(huì)判斷工作線程數(shù)是否超出最大線程數(shù)。如果沒(méi)有超出,會(huì)創(chuàng)建新的工作線程;如果已經(jīng)超出,則返回false,代表創(chuàng)建失敗;
  • 如果創(chuàng)建失敗,線程池執(zhí)行拒絕策略;
public class ThreadPoolExecutor 
{
  // 線程池執(zhí)行Runnable線程任務(wù)
  public void execute(Runnable command) {
    if (command == null)
      throw new NullPointerException();
    // 獲取線程池的狀態(tài)和工作線程數(shù)
    int c = ctl.get();
    // 工作線程的數(shù)量小于核心線程數(shù)
    if (workerCountOf(c) < corePoolSize) {
      // 創(chuàng)建新的Worker工作線程
      if (addWorker(command, true))
        return;
      // 創(chuàng)建失敗,重新獲取線程池的狀態(tài)和工作線程數(shù)
      c = ctl.get();
    }
    // 如果線程池處于RUNNING狀態(tài),緩存線程任務(wù)至工作隊(duì)列
    if (isRunning(c) && workQueue.offer(command)) {
      // 任務(wù)緩存成功
      // 重新獲取線程池的狀態(tài)和工作線程數(shù)
      int recheck = ctl.get();
      // 如果線程池不是處于RUNNING狀態(tài),則刪除任務(wù)
      if (! isRunning(recheck) && remove(command))
        // 執(zhí)行拒絕策略
        reject(command);
        // 如果工作線程數(shù)等于零
        // 通過(guò)addWorker()方法檢查線程池狀態(tài)和工作隊(duì)列
      else if (workerCountOf(recheck) == 0)
        addWorker(null, false);
    }
      // 如果緩存線程任務(wù)至工作隊(duì)列
      // 嘗試創(chuàng)建新的工作線程
      // 創(chuàng)建時(shí),判斷工作線程數(shù)是否超出最大線程數(shù)
      // 如果沒(méi)有超出,創(chuàng)建成功
      // 如果已經(jīng)超出,創(chuàng)建失敗
    else if (!addWorker(command, false))
      // 執(zhí)行拒絕策略
      reject(command);
  }
}

核心方法:addWorker()方法

execute()方法的執(zhí)行過(guò)程中,會(huì)通過(guò)addWorker()方法創(chuàng)建一個(gè)工作線程,用于執(zhí)行當(dāng)前線程任務(wù)。

閱讀源代碼,會(huì)發(fā)現(xiàn),這個(gè)方法的整個(gè)執(zhí)行過(guò)程可以分為兩個(gè)部分:檢查線程池的狀態(tài)和工作線程數(shù)量創(chuàng)建并執(zhí)行工作線程

1  檢查線程池的狀態(tài)和工作線程數(shù)量

private boolean addWorker(Runnable firstTask, boolean core) {
  // 第1部分:檢查線程池的狀態(tài)和工作線程數(shù)量
  // 循環(huán)檢查線程池的狀態(tài),直到符合創(chuàng)建工作線程的條件,通過(guò)retry標(biāo)簽break退出
  retry:
  for (;;) {
    // 通過(guò)ctl對(duì)象,獲取當(dāng)前線程池的運(yùn)行狀態(tài)
    int c = ctl.get();
    int rs = runStateOf(c);
    // 如果線程池處于開(kāi)始關(guān)閉的狀態(tài)(獲取線程任務(wù)為空,同時(shí)工作隊(duì)列不等于空)
    // 則工作線程創(chuàng)建失敗
    if (rs >= SHUTDOWN &&
        ! (rs == SHUTDOWN &&
           firstTask == null &&
           ! workQueue.isEmpty()))
      return false;
    // 檢查工作線程數(shù)量
    for (;;) {
      // 通過(guò)ctl對(duì)象,獲取當(dāng)前線程池中工作線程數(shù)量
      int wc = workerCountOf(c);
      // 工作線程數(shù)量如果超出最大容量或者核心線程數(shù)(最大線程數(shù))
      // 則工作線程創(chuàng)建失敗
      if (wc >= CAPACITY ||
          wc >= (core ? corePoolSize : maximumPoolSize))
        return false;
      // 通過(guò)ctl對(duì)象,將當(dāng)前工作線程數(shù)量+1,并通過(guò)retry標(biāo)簽break退出外層循環(huán)
      if (compareAndIncrementWorkerCount(c))
        break retry;
      // 再次獲取線程池狀態(tài),檢查是否發(fā)生變化
      c = ctl.get();  // Re-read ctl
      if (runStateOf(c) != rs)
        continue retry;
      // else CAS failed due to workerCount change; retry inner loop
    }
  }
  // 第2部分:創(chuàng)建并執(zhí)行工作線程....
}

2  創(chuàng)建并執(zhí)行工作線程

private boolean addWorker(Runnable firstTask, boolean core) {
  // 第1部分:檢查線程池的狀態(tài)和工作線程數(shù)量....
  // 第2部分:創(chuàng)建并執(zhí)行工作線程....
  boolean workerStarted = false; // 工作線程是否已經(jīng)啟動(dòng)
  boolean workerAdded = false;   // 工作線程是否已經(jīng)保存
  Worker w = null;
  try {
    // 創(chuàng)建新工作線程,并通過(guò)線程工廠創(chuàng)建Thread線程
    w = new Worker(firstTask);
    // 獲取新工作線程的Thread線程對(duì)象,用于啟動(dòng)真正的線程
    final Thread t = w.thread;
    if (t != null) {
      // 獲取線程池的ReentrantLock主鎖對(duì)象
      // 確保在添加和啟動(dòng)線程時(shí)的同步與安全
      final ReentrantLock mainLock = this.mainLock;
      mainLock.lock();
      try {
        // 檢查線程池狀態(tài)
        int rs = runStateOf(ctl.get());
        if (rs < SHUTDOWN ||
            (rs == SHUTDOWN && firstTask == null)) {
          // 檢查T(mén)hread線程對(duì)象的狀態(tài)是否已經(jīng)處于啟動(dòng)狀態(tài)
          if (t.isAlive()) 
            throw new IllegalThreadStateException();
          // 保存工作線程
          workers.add(w);
          // 記錄線程池曾經(jīng)達(dá)到過(guò)的最大工作線程數(shù)量
          int s = workers.size();
          if (s > largestPoolSize)
            largestPoolSize = s;
          workerAdded = true;
        }
      } finally {
        mainLock.unlock();
      }
      // 添加工作線程后,正式啟動(dòng)線程
      if (workerAdded) {
        t.start();
        workerStarted = true;
      }
    }
  } finally {
    if (! workerStarted)
      addWorkerFailed(w);
  }
  // 返回線程啟動(dòng)狀態(tài)
  return workerStarted;
}

到此這篇關(guān)于JAVA中關(guān)于多線程的學(xué)習(xí)和使用的文章就介紹到這了,更多相關(guān)java多線程使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決Error:Java:無(wú)效的源發(fā)行版:14問(wèn)題

    解決Error:Java:無(wú)效的源發(fā)行版:14問(wèn)題

    在項(xiàng)目開(kāi)發(fā)中,版本不一致常見(jiàn)問(wèn)題,首先,應(yīng)檢查本地JDK版本,使用命令java-version,其次,核對(duì)項(xiàng)目及模塊版本,若有不一致,通過(guò)修改pom.xml文件同步版本,重新下載依賴即可解決問(wèn)題,這種方法簡(jiǎn)單有效,適用于多種開(kāi)發(fā)環(huán)境
    2024-10-10
  • 阿里Sentinel支持Spring Cloud Gateway的實(shí)現(xiàn)

    阿里Sentinel支持Spring Cloud Gateway的實(shí)現(xiàn)

    這篇文章主要介紹了阿里Sentinel支持Spring Cloud Gateway的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-04-04
  • java實(shí)現(xiàn)用戶簽到BitMap功能實(shí)現(xiàn)demo

    java實(shí)現(xiàn)用戶簽到BitMap功能實(shí)現(xiàn)demo

    這篇文章主要為大家介紹了java實(shí)現(xiàn)用戶簽到BitMap功能實(shí)現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • 選擇java的理由:java之父訪談實(shí)錄

    選擇java的理由:java之父訪談實(shí)錄

    選擇java的理由:java之父訪談實(shí)錄...
    2006-12-12
  • redis做服務(wù)間通信工具的項(xiàng)目示例

    redis做服務(wù)間通信工具的項(xiàng)目示例

    Redis是一種高效的服務(wù)間通信工具,它以鍵值對(duì)的形式存儲(chǔ)數(shù)據(jù),并支持多種數(shù)據(jù)類(lèi)型和豐富的操作,本文主要介紹了redis做服務(wù)間通信工具的項(xiàng)目示例,感興趣的可以了解一下
    2023-08-08
  • springboot自動(dòng)裝配TypeNotPresentExceptionProxy異常排查解決

    springboot自動(dòng)裝配TypeNotPresentExceptionProxy異常排查解決

    這篇文章主要為大家介紹了springboot自動(dòng)裝配TypeNotPresentExceptionProxy異常排查解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Java常用的數(shù)據(jù)脫敏方法(手機(jī)、郵箱、身份證號(hào))

    Java常用的數(shù)據(jù)脫敏方法(手機(jī)、郵箱、身份證號(hào))

    這篇文章主要給大家介紹了關(guān)于Java常用的數(shù)據(jù)脫敏(手機(jī)、郵箱、身份證號(hào))的相關(guān)資料,信息脫敏對(duì)某些敏感信息通過(guò)脫敏規(guī)則進(jìn)行數(shù)據(jù)的變形,實(shí)現(xiàn)敏感隱私數(shù)據(jù)的可靠保護(hù),需要的朋友可以參考下
    2023-07-07
  • SpringBoot打成war包在tomcat或wildfly下運(yùn)行的方法

    SpringBoot打成war包在tomcat或wildfly下運(yùn)行的方法

    這篇文章主要介紹了SpringBoot打成war包在tomcat或wildfly下運(yùn)行的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-11-11
  • 基于Java編寫(xiě)第一個(gè)區(qū)塊鏈項(xiàng)目

    基于Java編寫(xiě)第一個(gè)區(qū)塊鏈項(xiàng)目

    區(qū)塊鏈?zhǔn)欠植际綌?shù)據(jù)存儲(chǔ)、點(diǎn)對(duì)點(diǎn)傳輸、共識(shí)機(jī)制、加密算法等計(jì)算機(jī)技術(shù)的新型應(yīng)用模式,下面這篇文章主要給大家介紹了基于Java實(shí)現(xiàn)區(qū)塊鏈的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • Java中雙冒號(hào)::的作用舉例詳解

    Java中雙冒號(hào)::的作用舉例詳解

    這篇文章主要給大家介紹了關(guān)于Java中雙冒號(hào)::作用的相關(guān)資料,雙冒號(hào)(::)運(yùn)算符在Java?8中被用作方法引用(method?reference),方法引用是與lambda表達(dá)式相關(guān)的一個(gè)重要特性,需要的朋友可以參考下
    2023-11-11

最新評(píng)論

原平市| 岳普湖县| 濮阳市| 克拉玛依市| 增城市| 拜泉县| 秭归县| 临桂县| 师宗县| 广宁县| 屏山县| 湖北省| 怀宁县| 阿荣旗| 寻甸| 灵丘县| 酉阳| 富民县| 淮南市| 沧源| 始兴县| 盐源县| 西乡县| 延津县| 田阳县| 广宗县| 旌德县| 海兴县| 晋江市| 灵川县| 万安县| 高要市| 绥棱县| 静乐县| 福海县| 广安市| 永康市| 淮阳县| 竹溪县| 衡水市| 陇川县|