線程、JMM回季、線程池家制、并發(fā)編程3大特性

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

進(jìn)程:是正在執(zhí)行的一段程序,一旦程序被載入內(nèi)存準(zhǔn)備執(zhí)行泡一,那就是一個(gè)進(jìn)程颤殴。進(jìn)程是表示資源分配的基本概念,又是調(diào)度運(yùn)算的基本單位鼻忠,是系統(tǒng)的并發(fā)執(zhí)行單元涵但。

線程:?jiǎn)蝹€(gè)進(jìn)程中執(zhí)行的每個(gè)任務(wù)都是一個(gè)線程,線程是進(jìn)程中執(zhí)行運(yùn)算的最小單位帖蔓。

線程的意義:提高程序效率矮瘟,充分發(fā)揮多和計(jì)算機(jī)的優(yōu)勢(shì)。

02 多線程創(chuàng)建

1塑娇,繼承Thread類澈侠,實(shí)現(xiàn)run();

public static class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("我的一個(gè)線程");
    }
}
public static void main(String[] args) {
    MyThread t = new MyThread();
    t.start();
}

2,實(shí)現(xiàn)Runnable接口埋酬,將對(duì)象做為Thread的構(gòu)造參數(shù)傳入

public static class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("我的一個(gè)線程");
    }
}
public static void main(String[] args) {
    Thread t = new Thread(new MyRunnable());
    t.start();
}

還有比較靈活的寫法:

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("匿名內(nèi)部類寫法");
    }
});
//JDK1.8版本以后
Thread t2 = new Thread(()->{
    System.out.println("蘭姆達(dá)表達(dá)式");
});
t1.start();
t2.start();

3哨啃,實(shí)現(xiàn)Callable接口的call(),允許線程執(zhí)行完以后獲取返回結(jié)果

public static class MyCall implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "返回結(jié)果";
    }
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<String> task = new FutureTask<String>(new MyCall());
    Thread thread = new Thread(task);
    thread.start();
    System.out.println(task.get());
}

03 用戶線程與守護(hù)線程

當(dāng)JVM運(yùn)行起來(lái)以后奇瘦,會(huì)有兩種線程:用戶線程與守護(hù)線程。

JVM關(guān)閉的條件:當(dāng)用戶線程運(yùn)行完畢后劲弦,即使存在守護(hù)線程耳标,它也會(huì)關(guān)閉。所以不能絕對(duì)的說(shuō):用戶線程和守護(hù)線程都運(yùn)行完畢了JVM才關(guān)閉邑跪。

在Java里有個(gè)很出名的守護(hù)線程就是:GC垃圾回收線程次坡。

做一個(gè)小測(cè)試:

public static void main(String[] args) throws ExecutionException, InterruptedException {
    Thread thread1 = new Thread(()->{
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("線程1:"+i);
        }
    });
    //守護(hù)線程
    thread1.setDaemon(true);
    thread1.start();
    //用戶線程
    Thread thread2 = new Thread(()->{
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("線程2:"+i);
        }
    });
    thread2.start();
}

輸出結(jié)果:

線程2:94
線程2:95
線程1:25
線程2:96
線程2:97
線程2:98
線程1:26
線程2:99
Process finished with exit code 0

上面的thread1.setDaemon(true);設(shè)置成了守護(hù)線程呼猪,當(dāng)用戶線程結(jié)束后,守護(hù)線程也停止運(yùn)行了砸琅。

04 線程的優(yōu)先級(jí)

CPU內(nèi)核同一時(shí)刻只能執(zhí)行一條線程宋距,內(nèi)核靠線程調(diào)度器來(lái)分配時(shí)間片來(lái)執(zhí)行線程。所以線程需要搶奪時(shí)間片來(lái)執(zhí)行症脂,那么優(yōu)先級(jí)其實(shí)就是設(shè)置搶奪的概率谚赎。

public static void main(String[] args){
    Thread thread1 = new Thread(()->{
        for (int i = 0; i < 100; i++) {
            System.out.println("線程1:"+i);
        }
    });
    Thread thread2 = new Thread(()->{
        for (int i = 0; i < 100; i++) {
            System.out.println("線程2:"+i);
        }
    });
    thread1.setPriority(10);
    thread2.setPriority(5);
    thread1.start();
    thread2.start();
}

輸出結(jié)果:

線程1:97
線程1:98
線程1:99
線程2:0
線程2:1
線程2:2
線程2:3
線程2:4

可以看到線程1執(zhí)行完畢后線程2才開始執(zhí)行,說(shuō)明線程1爭(zhēng)奪時(shí)間片的概率大诱篷。

05 線程的生命周期

線程的生命周期

06 join方法

在線程執(zhí)行的時(shí)候讓別的線程調(diào)用join先插隊(duì)壶唤,等別的線程執(zhí)行完后再執(zhí)行本線程。

public static void main(String[] args){
    Thread t1 = new Thread(()->{
        for (int i = 0; i < 3; i++) {
            System.out.println("t1:"+i);
        }
    });
    Thread t2 = new Thread(()->{
        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println("t2:"+i);
        }
    });
    Thread t3 = new Thread(()->{
        try {
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 3; i++) {
            System.out.println("t3:"+i);
        }
    });
    t1.start();
    t2.start();
    t3.start();
}

輸出結(jié)果:

t1:0
t1:1
t1:2
t2:0
t2:1
t2:2
t3:0
t3:1
t3:2
Process finished with exit code 0

07 JMM內(nèi)存模型

JMM(Java Memory Model)


JMM內(nèi)存模型

線程是不能夠直接修改主存內(nèi)的數(shù)據(jù)的棕所,而是先從主存中讀取到自己的工作內(nèi)存中創(chuàng)建副本闸盔,修改完成后寫入到主內(nèi)存,這就是JMM模型琳省。

這也是為什么多線程并發(fā)訪問(wèn)修改數(shù)據(jù)的時(shí)候?yàn)槭裁闯霈F(xiàn)安全問(wèn)題迎吵。

08 并發(fā)編程的三大特性

原子性

一個(gè)操作或多個(gè)操作,要么全部執(zhí)行并且執(zhí)行過(guò)程不被打斷针贬,要么全部不執(zhí)行(提供互斥訪問(wèn)击费,在同一時(shí)刻只有一個(gè)線程進(jìn)行訪問(wèn))

可以通過(guò)加鎖的方式。

先看不加鎖:

static int num = 100;
public static void main(String[] args){
    Runnable runnable = ()->{
        while (true){
            if(num>0){
                num--;
            }else{
                break;
            }
            System.out.println(num);
        }
    };
    new Thread(runnable).start();
    new Thread(runnable).start();
    new Thread(runnable).start();
}

輸出了詭異的結(jié)果:

1
0
98
94
Process finished with exit code 0

如果加鎖:

static int num = 100;
public static void main(String[] args){
    Object o = new Object();
    Runnable runnable = ()->{
        while (true){
            synchronized (o){
                if(num>0){
                    num--;
                }else{
                    break;
                }
                System.out.println(num);
            }
        }
    };
    new Thread(runnable).start();
    new Thread(runnable).start();
    new Thread(runnable).start();
}

輸出結(jié)果:

5
4
3
2
1
0
Process finished with exit code 0

加鎖需要傳入一個(gè)對(duì)象坚踩,本程序中該對(duì)象是唯一的荡灾,如果直接寫new Object()和不加鎖效果一樣。

可見性

public static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
    new Thread(()->{
        System.out.println("線程一號(hào)啟動(dòng)");
        while (!flag){};
        System.out.println("線程一號(hào)結(jié)束");
    }).start();
    Thread.sleep(1000);
    new Thread(()->{
        flag = true;
        System.out.println("把flag修改為了true");
    }).start();
}

顯然對(duì)于一號(hào)線程而言瞬铸,當(dāng)二號(hào)線程修改了flag的值后批幌,一號(hào)線程并沒(méi)有及時(shí)獲得flag,也就是說(shuō)flag對(duì)于一號(hào)線程是不可見的嗓节。flag的值沒(méi)有得到及時(shí)的更新荧缘。

有序性

JVM為了提高效率,會(huì)做出一些優(yōu)化拦宣,對(duì)指令進(jìn)行重排序(happens-before)截粗,在單線程的情況下沒(méi)有問(wèn)題,但是多線程編程需要考慮有序性問(wèn)題鸵隧。

volatile

1绸罗,保證可見性

2,屏蔽指令重排序

3豆瘫,但是保證不了原子性

synchronized

JDK1.0開始提供的關(guān)鍵字珊蟀,重量級(jí)的鎖,能夠保證某個(gè)代碼塊在被執(zhí)行時(shí)外驱,其他線程不訪問(wèn)執(zhí)行育灸。

synchronized必須使用對(duì)象做為鎖腻窒,因?yàn)閷?duì)象分為三部分:頭部分里面有個(gè)鎖的字段,synchronized就是利用該字段達(dá)到上鎖的目的磅崭。

注意:假如有兩個(gè)方法儿子,要想實(shí)現(xiàn)調(diào)用fun1時(shí)fun2不能被訪問(wèn),必須使用同一個(gè)對(duì)象加鎖

public static Object o ;
public static  void fun1(){
    synchronized (o){}
}
public static void fun2(){
    synchronized (o){}
}

Monitor

JVM 是通過(guò)進(jìn)入砸喻、退出對(duì)象監(jiān)視器(Monitor)來(lái)實(shí)現(xiàn)對(duì)方法柔逼,同步塊的同步。

使用synchronized加鎖定的代碼塊恩够,在被編譯后會(huì)形成對(duì)象監(jiān)視器的入口(monitorenter)和出口(monitorexit)


對(duì)象監(jiān)視器

使用對(duì)象Object做為鎖的時(shí)候卒落,當(dāng)有線程訪問(wèn)同步代碼塊的時(shí)候,監(jiān)視器入口(monitorenter)會(huì)去檢查Object是否上鎖蜂桶,沒(méi)有上鎖儡毕,則讓該線程訪問(wèn),同時(shí)給該對(duì)象上鎖扑媚,此時(shí)若有其他線程訪問(wèn)該代碼塊的時(shí)候腰湾,監(jiān)視器入口通過(guò)對(duì)比Object,發(fā)現(xiàn)上鎖了疆股,就會(huì)讓其他線程處于阻塞狀態(tài)费坊,當(dāng)?shù)跅l線程執(zhí)行完畢退出(monitorexir)以后,會(huì)給該對(duì)象解鎖旬痹,那么被阻塞的線程就可以接著訪問(wèn)附井,并保證了原子性。

public class Demo {
    public static synchronized void get(){
        //是把Demo.class(當(dāng)前類的字節(jié)碼文件)做為對(duì)象加鎖
    }
    public synchronized void to(){
        //是把this(當(dāng)前this鎖)做為對(duì)象枷鎖
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        demo.to();
        Demo.get();
    }
}

對(duì)象如同鎖两残,持有鎖的線程可以在同步中執(zhí)行永毅,沒(méi)有持有鎖的線程,即使獲取CPU的執(zhí)行權(quán)人弓,也進(jìn)不去沼死。

優(yōu)點(diǎn):解決了線程安全問(wèn)題

缺點(diǎn):多個(gè)線程需要判斷鎖,較為消耗資源崔赌,搶鎖的資源

09 J.U.C之Lock

Lock

public static void main(String[] args) {
    Lock lock = new ReentrantLock();
    //Lock是代碼實(shí)現(xiàn)的接口意蛀,而synchronized是JVM底層實(shí)現(xiàn)的
    new Thread(()->{
        lock.lock();
        try{
            //加鎖代碼塊
        }finally {
            //必須手動(dòng)釋放
            lock.unlock();
        }
    }).start();
}

trylock

上鎖部分不被訪問(wèn)的時(shí)候,去做別的事情健芭,而不是像synchronized那樣必須阻塞县钥。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo {
    Lock lock = new ReentrantLock();
    public void insert(Thread thread){
        if(lock.tryLock()){
            //搶到鎖了,就調(diào)用這里的方法
            try{
                System.out.println(thread.getName()+"搶到鎖啦");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println(thread.getName()+"釋放鎖啦");
            }
        }else{
            //如果沒(méi)有搶到鎖慈迈,就調(diào)用這里的方法
            System.out.println(Thread.currentThread()+"沒(méi)有搶到鎖啦");
        }
    }
    public static void main(String[] args) {
        Demo d = new Demo();
        new Thread(()->{
            d.insert(Thread.currentThread());
        }).start();
        new Thread(()->{
            d.insert(Thread.currentThread());
        }).start();
    }
}

Lock與synchronized的區(qū)別

1)Lock是一個(gè)接口若贮,而synchronized是一個(gè)關(guān)鍵字,是Java內(nèi)置的實(shí)現(xiàn),它可以直接修飾方法和代碼塊兜看,而lock只能修飾代碼塊。

2)synchronized在發(fā)生異常的時(shí)候狭瞎,會(huì)自動(dòng)釋放線程占有的鎖细移,因此不會(huì)導(dǎo)致死鎖現(xiàn)象,而Lock發(fā)生異常以后熊锭,如果沒(méi)有unLock()釋放鎖弧轧,很可能造成死鎖現(xiàn)象,因此在使用Lock的時(shí)候需要在finally中釋放鎖碗殷。

3)Lock可以讓等待的線程響應(yīng)中斷精绎,去干別的事情,而synchronized會(huì)讓線程一直阻塞下去锌妻。

4)通過(guò)Lock的trylock()可以知道有沒(méi)有成功獲取鎖代乃,而synchronized不行。

5)Lock可以提高多線程進(jìn)行讀操作的效率(提供讀寫鎖)仿粹。

從性能上來(lái)說(shuō)搁吓,如果競(jìng)爭(zhēng)資源很激烈,Lock的性能遠(yuǎn)遠(yuǎn)大于synchronized吭历。

10 線程通信

wait與notify使用兩個(gè)線程交替打印1-100堕仔,其中一條線程只打印奇數(shù),另外一個(gè)線程只打印偶數(shù)晌区。

public class Wait {
    static class Num{
        public int num = 1;//共享資源
    }
    static class J implements Runnable{
        public Num numObj;
        public J(Num n){
            this.numObj = n;
        }
        @Override
        public void run() {
            synchronized (numObj){
                while (numObj.num<=100){
                    if(numObj.num%2!=0){
                        System.out.println("奇數(shù)====>"+numObj.num);
                        numObj.num++;

                    }else {
                        try {
                            numObj.notify();
                            numObj.wait();//wait()要寫在代碼塊里面摩骨,因?yàn)樗淖饔檬轻尫沛i,并且等待
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    static class O implements Runnable{
        public Num numObj;
        public O(Num n){
            this.numObj = n;
        }
        @Override
        public void run() {
            synchronized (numObj){
            while (numObj.num<=100) {
                if (numObj.num % 2 == 0) {
                    System.out.println("偶數(shù)====>" + numObj.num);
                    numObj.num++;
                } else {
                    try {
                        numObj.notify();
                        numObj.wait();//wait()要寫在代碼塊里面朗若,因?yàn)樗淖饔檬轻尫沛i恼五,并且等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            }
        }
    }
    public static void main(String[] args) {
        Num numObj = new Num();
        new Thread(new J(numObj)).start();//奇數(shù)線程
        new Thread(new O(numObj)).start();//偶數(shù)線程
    }
}

wait()的作用是,釋放當(dāng)前鎖捡偏,并使當(dāng)前線程處于等待狀態(tài)唤冈,所以需要寫在同步代碼塊里,notify()是喚醒一個(gè)處于等待狀態(tài)的線程银伟,本題只有兩條線程你虹,如果有多條線程,那么可以使用notifyAll()彤避。

這三個(gè)方法最終調(diào)用的都是JVM級(jí)的native方法傅物。

11 什么是線程池

線程池是Java的并發(fā)框架,幾乎所有的并發(fā)執(zhí)行程序或者異步操作都可以使用線程池琉预。

1)降低資源消耗董饰。通過(guò)重復(fù)使用已經(jīng)創(chuàng)建好的線程降低創(chuàng)建和銷毀的消耗。

2)提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí)卒暂,不需要等到線程創(chuàng)建后就可以立即執(zhí)行啄栓。

3)提高現(xiàn)成的可管理性。使用線程池可以進(jìn)行統(tǒng)一的分配也祠、調(diào)優(yōu)昙楚、監(jiān)控。

12 線程池工作原理

Executor是最基本的接口诈嘿,其子接口:ExecutorService是工作中常用的接口
其中一個(gè)很重要的實(shí)現(xiàn)類是ThreadPoolExecutor堪旧,通過(guò)它new出來(lái)
import java.util.concurrent.*;

public class Pool {
     static class MyRunnable implements Runnable{
        String info = "";
        public MyRunnable(String txt){
            this.info = txt;
        }
        @Override
        public void run() {
            System.out.println(this.info);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
//        int corePoolSize      核心線程數(shù)
//        int maximumPoolSize   最大線程數(shù)
//        long keepAliveTime    保持存活得時(shí)間
//        TimeUnit unit         時(shí)間單位
//        BlockingQueue<Runnable> workQueue     任務(wù)隊(duì)列
//        RejectedExecutionHandler handler      飽和策略
        ExecutorService es = new ThreadPoolExecutor(
                2,//corePoolSize
                5,//maximumPoolSize
                10,//keepAliveTime
                TimeUnit.SECONDS,//unit
                new ArrayBlockingQueue<Runnable>(5),
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 1; i <= 20; i++) {
            try{
                es.execute(new MyRunnable("執(zhí)行第"+i+"條線程"));
            }catch (Throwable e){
                System.out.println("丟棄線程"+i);
            }
        }
        es.shutdown();
    }
}
線程池

四個(gè)流程:

1)判斷核心線程數(shù)

2)判斷任務(wù)隊(duì)列

3)判斷最大線程數(shù)(備胎線程)

4)執(zhí)行飽和策略

存活時(shí)間參數(shù):當(dāng)最大線程數(shù)(備胎線程)執(zhí)行完畢,并且沒(méi)有新任務(wù)的前提下奖亚,只能存活(keepAliveTime unit )的時(shí)間淳梦,超過(guò)時(shí)間就會(huì)被釋放掉。

13 三種常見隊(duì)列

SynchronousQueue

一次性只能裝一個(gè)任務(wù)昔字,其他任務(wù)處于阻塞狀態(tài)爆袍,同時(shí)一次性只能取出一個(gè)任務(wù),如果沒(méi)有任務(wù)可以取出作郭,也處于阻塞狀態(tài)

import java.util.concurrent.SynchronousQueue;

public class Queue {
    public static void main(String[] args) {
        SynchronousQueue<Integer> queue = new SynchronousQueue<>();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    queue.put(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("裝入數(shù)據(jù)====>"+i);
            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    System.out.println("取出數(shù)據(jù)=====>"+queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
//執(zhí)行結(jié)果
取出數(shù)據(jù)=====>0
裝入數(shù)據(jù)====>0
    隔了兩秒
取出數(shù)據(jù)=====>1
裝入數(shù)據(jù)====>1
    隔了兩秒
取出數(shù)據(jù)=====>2
裝入數(shù)據(jù)====>2‘
    隔了兩秒
取出數(shù)據(jù)=====>3
裝入數(shù)據(jù)====>3

LinkedBlockingQueue

瞬間裝完所有的任務(wù)螃宙,然后慢慢取出

import java.util.concurrent.LinkedBlockingQueue;

public class Queue {
    public static void main(String[] args) {
        LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    queue.put(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("裝入數(shù)據(jù)====>"+i);
            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    System.out.println("取出數(shù)據(jù)=====>"+queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
//執(zhí)行結(jié)果
裝入數(shù)據(jù)====>18
裝入數(shù)據(jù)====>19
    瞬間裝完20個(gè)數(shù)據(jù)
取出數(shù)據(jù)=====>0
    隔了兩秒
取出數(shù)據(jù)=====>1
    隔了兩秒
取出數(shù)據(jù)=====>2
    隔了兩秒
取出數(shù)據(jù)=====>3

ArrayBlockingQueue

設(shè)置填裝大小,比如4所坯,那么一次性裝入4個(gè)谆扎,其余的取出一個(gè)就裝一個(gè)

import java.util.concurrent.ArrayBlockingQueue;

public class Queue {
    public static void main(String[] args) {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(4);
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    queue.put(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("裝入數(shù)據(jù)====>"+i);
            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    System.out.println("取出數(shù)據(jù)=====>"+queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
//執(zhí)行結(jié)果
裝入數(shù)據(jù)====>0
裝入數(shù)據(jù)====>1
裝入數(shù)據(jù)====>2
裝入數(shù)據(jù)====>3
    瞬間完成
取出數(shù)據(jù)=====>0
裝入數(shù)據(jù)====>4
    隔了兩秒
取出數(shù)據(jù)=====>1
裝入數(shù)據(jù)====>5
    隔了兩秒

14 飽和策略

CallerRunsPolicy
    不拋棄任務(wù),調(diào)用線程池的線程芹助,幫助執(zhí)行任務(wù)
    比如上面的main方法調(diào)用的線程池堂湖,16號(hào)線程就會(huì)在main中執(zhí)行
演示代碼:
import java.util.concurrent.*;

public class Pool {
     static class MyRunnable implements Runnable{
        String info = "";
        public MyRunnable(String txt){
            this.info = txt;
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+":"+this.info);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        ExecutorService es = new ThreadPoolExecutor(
                2,
                5,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(10),
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 1; i <= 20; i++) {
            try{
                es.execute(new MyRunnable("執(zhí)行第"+i+"條線程"));
            }catch (Throwable e){
                System.out.println("丟棄線程"+i);
            }
        }
        es.shutdown();
    }
}
執(zhí)行結(jié)果:
pool-1-thread-2:執(zhí)行第2條線程
pool-1-thread-1:執(zhí)行第1條線程
main:執(zhí)行第16條線程
pool-1-thread-3:執(zhí)行第13條線程
pool-1-thread-4:執(zhí)行第14條線程
pool-1-thread-5:執(zhí)行第15條線程
AbortPolicy(默認(rèn))
    當(dāng)最大線程數(shù)滿了以后,拋出異常状土,拋棄任務(wù)
DiscardPolicy
    連異常都不拋无蜂,直接把任務(wù)丟了
DiscardOldestPolicy
    連異常都不拋,直接把任務(wù)丟了

15 線程池工具類

工具類:Executors 快速創(chuàng)建線程池

緩存線程池

newCachedThreadPool

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Es {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            System.out.println("線程"+i);
            executorService.execute(()->{
                for (int j = 0; j < 5; j++) {
                    System.out.println(Thread.currentThread().getName()+":"+j);
                }
            });
        }
        executorService.shutdown();
    }
}

源碼的實(shí)現(xiàn):

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

可以發(fā)現(xiàn):沒(méi)有核心線程數(shù)蒙谓,扔進(jìn)去多少線程斥季,就會(huì)創(chuàng)建多少線程,好處是線程復(fù)用以及可以及時(shí)釋放線程累驮,弊端就是酣倾,當(dāng)任務(wù)量極大的時(shí)候,他就創(chuàng)建一大堆線程谤专。

定長(zhǎng)線程池

newFixedThreadPool

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Es {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 10; i++) {
            System.out.println("線程"+i);
            executorService.execute(()->{
                for (int j = 0; j < 5; j++) {
                    System.out.println(Thread.currentThread().getName()+":"+j);
                }
            });
        }
        executorService.shutdown();
    }
}

源碼的實(shí)現(xiàn):

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

單例線程池

newSingleThreadExecutor

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Es {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            System.out.println("線程"+i);
            executorService.execute(()->{
                for (int j = 0; j < 5; j++) {
                    System.out.println(Thread.currentThread().getName()+":"+j);
                }
            });
        }
        executorService.shutdown();
    }
}

可以發(fā)現(xiàn)單例線程池躁锡,會(huì)由一個(gè)線程完成所有的任務(wù)。

源碼的實(shí)現(xiàn):

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

調(diào)度線程池

ScheduledExecutorService:newScheduledThreadPool

可以實(shí)現(xiàn):延遲執(zhí)行的線程池

public static void main(String[] args) {
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
    //延遲執(zhí)行的線程池
    scheduledExecutorService.schedule(()->{
        System.out.println("過(guò)去5秒種啦");
    },5, TimeUnit.SECONDS);//第二個(gè)參數(shù)是延遲時(shí)間置侍,然后是時(shí)間單位
    scheduledExecutorService.shutdown();
}

可以實(shí)現(xiàn):周期執(zhí)行的線程池

public static void main(String[] args) {
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
    //周期執(zhí)行的線程池:5s之后開始執(zhí)行任務(wù)映之,每隔2s重復(fù)執(zhí)行一次
    scheduledExecutorService.scheduleAtFixedRate(()->{
        System.out.println("執(zhí)行啦");
    },5,2, TimeUnit.SECONDS);//參數(shù):延遲時(shí)間 間隔時(shí)間 時(shí)間單位
    //scheduledExecutorService.shutdown();這時(shí)候不要shutdown
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拦焚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子杠输,更是在濱河造成了極大的恐慌赎败,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蠢甲,死亡現(xiàn)場(chǎng)離奇詭異螟够,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)峡钓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)若河,“玉大人能岩,你說(shuō)我怎么就攤上這事∠舾#” “怎么了拉鹃?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鲫忍。 經(jīng)常有香客問(wèn)我膏燕,道長(zhǎng),這世上最難降的妖魔是什么悟民? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任坝辫,我火速辦了婚禮,結(jié)果婚禮上射亏,老公的妹妹穿的比我還像新娘近忙。我一直安慰自己,他們只是感情好智润,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布及舍。 她就那樣靜靜地躺著,像睡著了一般窟绷。 火紅的嫁衣襯著肌膚如雪锯玛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天兼蜈,我揣著相機(jī)與錄音攘残,去河邊找鬼。 笑死为狸,一個(gè)胖子當(dāng)著我的面吹牛肯腕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钥平,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼实撒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼姊途!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起知态,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤捷兰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后负敏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贡茅,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年其做,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了顶考。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妖泄,死狀恐怖驹沿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蹈胡,我是刑警寧澤渊季,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站罚渐,受9級(jí)特大地震影響却汉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜荷并,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一合砂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧源织,春花似錦既穆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至黎茎,卻和暖如春囊颅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背傅瞻。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工踢代, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗅骄。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓胳挎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親溺森。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慕爬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容