Java 多線程基礎(chǔ)

進程:正在執(zhí)行中的程序,其實是應(yīng)用程序在內(nèi)存中運行的那片空間导梆。

線程:進程中的一個執(zhí)行單元,負(fù)責(zé)進程中程序的執(zhí)行火的。一個進程中至少有一個線程赠法,也可以有多個線程麦轰,此時稱為多線程程序。

硬盤:持久化存儲數(shù)據(jù)區(qū)域(關(guān)機后數(shù)據(jù)仍在)砖织。

內(nèi)存:臨時性存儲數(shù)據(jù)區(qū)域(關(guān)機后數(shù)據(jù)消失)款侵,提高性能。

CPU處理程序是通過快速切換完成的侧纯,與我們來說是隨機的新锈;多線程的使用可以合理的使用CPU資源,如果線程過多會導(dǎo)致降低性能眶熬。

Thread 的相關(guān)方法

  • Thread.currentThread().getName(): 獲得當(dāng)前線程的名稱(主線程:main妹笆;自定義線程:Thread-N)。
  • isAlive:判斷線程是否未終止
  • getPriority:獲得線程的優(yōu)先級數(shù)值
  • setPriority:設(shè)置線程的優(yōu)先級數(shù)值
  • setName:設(shè)置線程的名字

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

一聋涨、繼承Thread類

  1. 繼承Thread
  2. 重寫Threadrun方法晾浴。
  3. 創(chuàng)建子類對象负乡,即線程對象
  4. 調(diào)用start方法牍白,開啟線程并讓線程執(zhí)行,同時告訴jvm去調(diào)用run方法抖棘。
class Demo extends Thread{
    private String name;

    public Demo(String name) {
        super();
        this.name = name;
    }

    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println("====="+ Thread.currentThread().getName() +"=====" + this.name + i);
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        // 創(chuàng)建了兩個線程對象
        Demo d1 = new Demo("張三");
        Demo d2 = new Demo("李四");
        d2.start();//將d2線程開啟
        d1.run();// 由主線程負(fù)責(zé)
    }
}

問題

線程對象調(diào)用run方法和調(diào)用start方法的區(qū)別茂腥?
調(diào)用run方法不開啟線程,僅是對象調(diào)用方法.
調(diào)用start開啟線程,并讓jvm調(diào)用run方法在開啟的線程中執(zhí)行.

多線程內(nèi)存

  • 多線程執(zhí)行時,在棧內(nèi)存中,每一個線程都有一片屬于自己的棧內(nèi)存空間,進行方法的壓棧和彈棧.
  • 當(dāng)執(zhí)行線程的任務(wù)結(jié)束了,線程自動在棧內(nèi)存中釋放.
  • 當(dāng)所有的執(zhí)行線程都結(jié)束了,進程才結(jié)束

二、實現(xiàn) Runnable 接口

  1. 定義類實現(xiàn)Runnable接口: 避免繼承Thread類的單繼承局限性
  2. 覆蓋接口中的run方法切省。將線程任務(wù)代碼定義到run方法中
  3. 創(chuàng)建Thread類的對象最岗,并將Runnable接口的子類對象作為參數(shù)傳遞給Thread類的構(gòu)造函數(shù)。因為線程被封裝到Runnable接口的run方法中朝捆,而這個run方法所屬于Runnable接口的子類對象般渡,所以將這個子類對象作為參數(shù)傳遞給THread的構(gòu)造函數(shù)。這樣,線程對象創(chuàng)建時就可以明確要運行的線程任務(wù)驯用。
  4. 調(diào)用Thread類的start方法開啟線程
class Demo implements Runnable{
    private String name;

    public Demo(String name) {
        super();
        this.name = name;
    }
   // 覆蓋了接口Runnable中的run方法 
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println("====="+ Thread.currentThread().getName() +"=====" + this.name + i);
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
       // 創(chuàng)建Runnable子類的對象脸秽,注意它并不是線程對象
        Demo d1 = new Demo("張三");
        Demo d2 = new Demo("李四");
        Thread t1 = new Thread(d1); 
        Thread t2 = new Thread(d2); 
        t1.start();
        t2.start();
        System.out.println(Thread.currentThread().getName());
    }
}

優(yōu)勢

  • 實現(xiàn)Runnable接口避免了單繼承的局限性,所以較為常用蝴乔。
  • 實現(xiàn)Runnable接口的方式记餐,更加符合面向?qū)ο蟆>€程分為兩部分薇正,一部分線程對象片酝,一部分線程任務(wù)。
    • 繼承Thread類挖腰,線程對象和任務(wù)耦合在一起雕沿,一旦創(chuàng)建Thread子類對象,即使線程對象猴仑,又是線程任務(wù)晦炊。
    • 實現(xiàn)Runnable接口,將線程任務(wù)單獨分離出來封裝成對象宁脊,類型就是Runnable接口断国,實現(xiàn)了解耦。

線程狀態(tài)

多線程的安全問題

由于線程的隨機性榆苞,會出現(xiàn)多線程的安全問題稳衬。

原因

  • 線程任務(wù)操作共享的數(shù)據(jù)
  • 線程任務(wù)操作數(shù)據(jù)的運算有多個

解決

1、synchronized 同步代碼塊

synchronized(對象){
    // 需要被同步的代碼
}

原理

線程1讀到synchronized坐漏,會找后面括號中的對象(可任意薄疚,一般寫this)并拿到該對象,之后往下執(zhí)行赊琳。當(dāng)線程2讀到synchronized的時候街夭,也會找后面括號中的對象,發(fā)現(xiàn)被線程1拿走了躏筏,所以線程2進不來了板丽。直到線程1執(zhí)行完synchronized代碼塊并釋放對象之后,線程2才能繼續(xù)執(zhí)行趁尼。(對象相當(dāng)于鎖)-->火車上的衛(wèi)生間

注意:必須保證多個線程在同步中使用的同一個鎖埃碱,即synchronized后面括號中為同一個對象

同步弊端:降低了程序性能。

2酥泞、 同步函數(shù)

同步函數(shù)使用的鎖是固定的this砚殿。當(dāng)線程任務(wù)只需要一個同步時完全可以使用同步函數(shù)。
同步代碼塊使用的鎖是任意對象芝囤。當(dāng)線程中需要多個同步時 似炎,必須通過鎖來區(qū)分(較為常用)

public synchronized void method(){
    // 需要被同步的代碼
}

注意:static同步函數(shù)public static synchronized void method(){}辛萍,使用的鎖不是this,而是字節(jié)碼文件對象(類名.class)羡藐。因為萬物皆對象叹阔,字節(jié)碼文件也被視為對象存在。因此相應(yīng)的synchronized代碼塊后的對象也要用類名.class:synchronized(類名.class){}.

分析

  • 既然是多線程的問題传睹,必然發(fā)生在線程任務(wù)內(nèi)
  • 分析線程任務(wù)內(nèi)是否有共享數(shù)據(jù)
  • 是否有對數(shù)據(jù)進行多次運算

懶漢式線程安全問題

惡漢式:線程安全耳幢,調(diào)用率高,但是不能延時加載欧啤,類初始化時睛藻,立即加載這個對象

public class Demo01 {
    private static Demo01 instance = new Demo01();
    private Demo01() { }
    public static Demo01 getInstance() {
        return instance;
    }
}

懶漢式:可以延時加載,存在線程問題邢隧,可以加鎖店印,并且為了兼顧效率,再加一次判斷倒慧,減少判斷鎖的次數(shù)

public class Single {
    private static Single instance;
    private Single() { }
    public static  Single getInstance() {
        if(instance == null){
            synchronized(Single.class){
                if (instance == null) {
                instance = new Single();
             }
            }
        }
        return instance;
    }
}

死鎖

當(dāng)線程任務(wù)中出現(xiàn)了多個同步(多個鎖)時按摘,如果同步中嵌套了其他同步,容易引發(fā)死鎖纫谅。如下:

//Thread_0
synchronized(obj1){
    //Thread-0 obj1-->
    synchronized(obj2){
    }
}

//Thread_1
synchronized(obj2){
    //Thread-1 obj2-->
    synchronized(obj1){
    }
}

一個死鎖程序

public class DeadLock {
    public static void main(String[] args) {
        Test t1 = new Test(true);
        Test t2 = new Test(false);
        Thread t11 = new Thread(t1);
        Thread t22 = new Thread(t2);
        t11.start();
        t22.start();
    }
}

class Test implements Runnable {
    private boolean flag = false;

    Test(boolean flag) {
        this.flag = flag;
    }

    public void run() {
        if (flag) {
            while (true) {
                synchronized (MyLock.LOCKA) {
                    System.out.println(Thread.currentThread().getName() + "...if...lock a");
                    synchronized (MyLock.LOCKB) {
                        System.out.println(Thread.currentThread().getName() + "...if...lock b");
                    }
                }
            }

        } else {
            while (true) {
                synchronized (MyLock.LOCKB) {
                    System.out.println(Thread.currentThread().getName() + "...if...lock b");
                    synchronized (MyLock.LOCKA) {
                        System.out.println(Thread.currentThread().getName() + "...if...lock a");
                    }
                }
            }

        }
    }
}

class MyLock {
    public static final Object LOCKA = new Object();
    public static final Object LOCKB = new Object();
}

多線程通訊

生產(chǎn)者消費者問題

這是多線程中最為常見的案例(重要)
生產(chǎn)者和消費者同時執(zhí)行炫贤,需要多線程;但是任務(wù)卻不相同付秕,處理的資源是相同的:線程間的通信

生產(chǎn)消費實例:

public class ProduceConsumer {
    public static void main(String[] args) {
        Resource r = new Resource();
        Produce produce = new Produce(r);
        Consumer consumer = new Consumer(r);
        Thread t1 = new Thread(produce);
        Thread t11 = new Thread(produce);
        Thread t2 = new Thread(consumer);
        Thread t22 = new Thread(consumer);
        // 開啟多個生產(chǎn)多個消費
        t1.start();
        t11.start();
        t2.start();
        t22.start();
    }
}

// 資源
class Resource {
    private String name;
    private int num = 1;
    private boolean flag = false;

    // 生產(chǎn)
    public synchronized void set(String name) {
        while (flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.name = name + num;
        num++;
        System.out.println(Thread.currentThread().getName() + "---生產(chǎn)者---" + this.name);
        flag = true;
        notifyAll();
    }

    // 消費
    public synchronized void get() {
        while (!flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "---消費者---" + this.name);
        flag = false;
        notifyAll();
    }
}

// 生產(chǎn)者
class Produce implements Runnable {
    private Resource r;

    public Produce(Resource r) {
        this.r = r;
    }

    public void run() {
        for (int i = 50; i < 200; i++) {
            r.set("面包"); // 開始生產(chǎn)
        }
    }
}

// 消費者
class Consumer implements Runnable {
    private Resource r;

    public Consumer(Resource r) {
        this.r = r;
    }

    public void run() {
        for (int i = 0; i < 200; i++) {
            r.get(); // 開始消費
        }
    }
}

注意:

  • 當(dāng)多個生產(chǎn)消費的時候兰珍,為防止被喚醒的線程沒有判斷標(biāo)記,要用while判斷標(biāo)記询吴,而不是if掠河。
  • while時會出現(xiàn)死鎖,因為本方喚醒了本方,希望喚醒對方猛计,所以使用notifyAll方法唠摹。

等待喚醒機制

  • wait(): 會讓線程處于等待狀態(tài),將線程臨時存進了線程池中
  • notify(): 會喚醒線程池中的任意一個等待線程奉瘤。
  • notifyAll(): 會喚醒線程池中所有的等待線程勾拉。

注意:

  • 這些方法必須使用在同步中,因為必須要標(biāo)識wait毛好、notify等方法所使用的鎖望艺。同一個鎖上的notify苛秕,只能喚醒該鎖上的wait方法肌访。
  • 這些方法必須標(biāo)識所屬的鎖,而鎖可以是任意對象艇劫,任意對象可以調(diào)用的方法必須是Object的方法吼驶,所以這些方法定義在Object類中

Lock

在 JDK 1.5 之后,Lock 實現(xiàn)提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。

Lock l = ...; 
     l.lock();
     try {
         // access the resource protected by this lock
     } finally {
         l.unlock(); // 因為必須要釋放鎖蟹演,所以放到finally中
     }

因為必須要釋放鎖风钻,所以lock.lock()放到finally 塊中。

之前用synchronized同步酒请,鎖可以是任意對象骡技,并且鎖對象和鎖的方法是在一塊的(Object 對象中的object()notify()羞反、notifyAll()方法)布朦,而在Lock中把所有的監(jiān)視器方法封裝到Condition 對象中,實現(xiàn)了鎖對象和監(jiān)視器方法(鎖方法)的分離昼窗,更加的面向?qū)ο蟆?/p>

Lock lock = new ReentrantLock(); // 獲得鎖對象
Condition con = lock.newCondition(); // 獲得lock上的監(jiān)視器方法對象
lock.lock();    // 得到鎖
con.await();    //  讓線程處于等待狀態(tài)
con.signal();   // 喚醒任意一個等待的線程
con.singnalAll(); // 喚醒所有等待的線程
lock.unlock();  // 釋放鎖

一個實例

class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }

多線程的細(xì)節(jié)問題

1. sleep 和 wait 方法的異同點

  • 相同點
    • 都可以讓線程處于凍結(jié)狀態(tài)
  • 不同點
    • sleep 必須指定時間是趴;wait 可以指定時間,也可以不指定時間
    • sleep 時間到澄惊,線程處于臨時阻塞或者運行唆途;wait 如果沒指定時間,必須通過notify 或者 notifyAll喚醒掸驱。
    • sleep 不一定非要定義在同步中肛搬;wait 必須定義在同步中。
    • 都定義在同步中
      • 線程執(zhí)行到 sleep毕贼,不會釋放所
      • 線程執(zhí)行到 wait滚婉,會釋放鎖

2. 線程如何停止

所謂線程結(jié)束,就是讓線程任務(wù)代碼完成帅刀,run方法結(jié)束让腹。

  • stop 方法(過時):具有固定的不安全性,用Thread.stop來終止線程扣溺,將釋放它已經(jīng)鎖定的所有監(jiān)視器骇窍。
  • 定義循環(huán),控制住循環(huán)就行了
  • 如果目標(biāo)線程等待很長時間(處于凍結(jié)狀態(tài))锥余,應(yīng)用interrupt方法中斷該線程(將線程的凍結(jié)狀態(tài)清除腹纳,讓線程重新獲得cpu的執(zhí)行資格),并且收到一個InterruptException驱犹,在catch塊中捕獲嘲恍,在異常處理中改變標(biāo)記,讓循環(huán)結(jié)束雄驹。

Interrupt 實例

package thread;

class Task implements Runnable {
    boolean flag = true;
    public synchronized void run() {
        while (flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "-->" + e.toString());
                changeFlag();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
    public void changeFlag() {
        flag = false;
    }
}
public class InterruptDemo {
    public static void main(String[] args) {
        Task d = new Task();
        Thread t1 = new Thread(d,"線程1");
        Thread t2 = new Thread(d,"線程2");
        t1.start();
        t2.start();
        int x = 0;
        while (true) {
            if (++x == 50) {
                // d.changeFlag();
                t1.interrupt();
                t2.interrupt();
                break;
            }
            System.out.println(Thread.currentThread().getName());
        }
        System.out.println("over...");
    }
}

3. 守護線程

守護線程佃牛,可以理解為后臺線程,一般創(chuàng)建的為前臺線程医舆,前后臺運行線程的時候都是一樣的俘侠,獲取cpu的執(zhí)行權(quán)限象缀。但是結(jié)束的時候有些不同,前臺線程和后臺線程只要run方法結(jié)束爷速,線程結(jié)束央星,但是在所有前臺線程結(jié)束的時候,后臺線程無論處于什么狀態(tài)都會結(jié)束惫东,從而進程結(jié)束莉给。進程結(jié)束依賴的都是前臺線程。

方法: setDaemon(boolean on)

  • 該方法必須在線程啟動前調(diào)用:t.setDaemon(true); t.start; // t 線程設(shè)置為了守護線程
  • on如果為true廉沮,該線程標(biāo)記為守護線程

4. 線程的優(yōu)先級

Thread.currentThread.toString: 返回該線程的字符串表示形式禁谦,包括『線程名稱』、『優(yōu)先級』废封、『線程組』

優(yōu)先級:

  • 用數(shù)字標(biāo)識的0-10州泊;其中默認(rèn)的初始化優(yōu)先級是5;
  • 最明顯的三個優(yōu)先級 : 1漂洋,5遥皂,10。
  • Thread.MAX_PRIORITY 線程可以具有的最高優(yōu)先級刽漂。
  • Thread.MIN_PRIORITY 線程可以具有的最低優(yōu)先級演训。
  • Thread.NORM_PRIORITY 分配給線程的默認(rèn)優(yōu)先級。
  • 得到線程的優(yōu)先級:getPriority()
  • 更改線程的優(yōu)先級:setPriority()

5. 線程組

ThreadGroup: 可以通過Thread構(gòu)造函數(shù)明確新線程對象所屬的線程組

線程組的好處: 可以對多個同組線程贝咙,進行統(tǒng)一的操作样悟。默認(rèn)都屬于main線程。

6. join() 和 yield() 方法

join() 方法

用于臨時加入一個運算的線程庭猩。讓該線程執(zhí)行完窟她,程序才會執(zhí)行。

Demo d  = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
try{
    // 主線程執(zhí)行到這里蔼水,知道t1要加入執(zhí)行震糖,主線程釋放了執(zhí)行權(quán)(僅僅是釋放,至于執(zhí)行權(quán)給誰趴腋,有cpu隨機決定)
    // 主線程的執(zhí)行資格處于凍結(jié)狀態(tài)吊说,直至t1線程執(zhí)行完恢復(fù)
    t1.join;
    }catch(InterruptException e){}
t2.start();

yield() 方法

暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程优炬。

class Demo implements Runnable{
    public void run(){
        for(int i = 0; i < 30; i++){
            // 線程臨時停止颁井,將執(zhí)行權(quán)釋放,讓其他線程有機會獲得執(zhí)行權(quán)
            Thread.yield();
        }
    }
}

線程中匿名內(nèi)部類使用

Runnable r = new Runnable(){
    public void run (){
        code....
    }    
};
new Thread(r).start();

new Thread(){
    public void run (){
        code....
}      
}.start();

哪一個執(zhí)行蠢护?

new Thread(new Runnable(){
        public void run(){
            System.out.println("runnable run");
        }
    }){
        public void run(){
            System.out.println("subthread run"); // 執(zhí)行
        }
    }.start();

線程池

自JDK5之后雅宾,Java推出了一個并發(fā)包java.util.concurrent,在Java開發(fā)中糊余,我們接觸到了好多池的技術(shù)秀又,String類的對象池单寂、Integer的共享池贬芥、連接數(shù)據(jù)庫的連接池吐辙、Struts1.3的對象池等等,池的最終目的都是節(jié)約資源蘸劈,以更小的開銷做更多的事情昏苏,從而提高性能。

我們的web項目都是部署在服務(wù)器上威沫,瀏覽器端的每一個request就是一個線程贤惯,那么服務(wù)器需要并發(fā)的處理多個請求,就需要線程池技術(shù)棒掠,下面來看一下Java并發(fā)包下如何創(chuàng)建線程池孵构。

  1. 創(chuàng)建一個可重用固定線程集合的線程池,以共享的無界隊列方式來運行這些線程烟很。
ExecutorService threadPool = Executors.newFixedThreadPool(3);// 創(chuàng)建可以容納3個線程的線程池
  1. 創(chuàng)建一個可根據(jù)需要創(chuàng)建新線程的線程池颈墅,但是在以前構(gòu)造的線程可用時將重用它們。
ExecutorService threadPool = Executors.newCachedThreadPool();// 線程池的大小會根據(jù)執(zhí)行的任務(wù)數(shù)動態(tài)分配
  1. 創(chuàng)建一個使用單個 worker 線程的 Executor雾袱,以無界隊列方式來運行該線程恤筛。
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 創(chuàng)建單個線程的線程池,如果當(dāng)前線程在執(zhí)行任務(wù)時突然中斷芹橡,則會創(chuàng)建一個新的線程替代它繼續(xù)執(zhí)行任務(wù)
  1. 創(chuàng)建一個可安排在給定延遲后運行命令或者定期地執(zhí)行的線程池毒坛。
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3); // 效果類似于Timer定時器

每種線程池都有不同的使用場景,下面看一下這四種線程池使用起來有什么不同林说。

  1. FixedThreadPool
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
publicclass ThreadPoolTest {  
public static void main(String[] args) {  
    ExecutorService threadPool = Executors.newFixedThreadPool(3);  
    for(int i = 1; i < 5; i++) {  
        final int taskID = i;  
        threadPool.execute(new Runnable() {  
            public void run() {  
                for(int i = 1; i < 5; i++) {  
                    try {  
                        Thread.sleep(20);// 為了測試出效果煎殷,讓每次任務(wù)執(zhí)行都需要一定時間
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                        System.out.println("第" + taskID + "次任務(wù)的第" + i + "次執(zhí)行");  
                    }  
                }  
            });  
        }  
        threadPool.shutdown();// 任務(wù)執(zhí)行完畢,關(guān)閉線程池
    }  
}

輸出結(jié)果:

第1次任務(wù)的第1次執(zhí)行  
第2次任務(wù)的第1次執(zhí)行  
第3次任務(wù)的第1次執(zhí)行  
第2次任務(wù)的第2次執(zhí)行  
第3次任務(wù)的第2次執(zhí)行  
第1次任務(wù)的第2次執(zhí)行  
第3次任務(wù)的第3次執(zhí)行  
第1次任務(wù)的第3次執(zhí)行  
第2次任務(wù)的第3次執(zhí)行  
第3次任務(wù)的第4次執(zhí)行  
第2次任務(wù)的第4次執(zhí)行  
第1次任務(wù)的第4次執(zhí)行  
第4次任務(wù)的第1次執(zhí)行  
第4次任務(wù)的第2次執(zhí)行  
第4次任務(wù)的第3次執(zhí)行  
第4次任務(wù)的第4次執(zhí)行  

上段代碼中腿箩,創(chuàng)建了一個固定大小的線程池蝌数,容量為3,然后循環(huán)執(zhí)行了4個任務(wù)度秘,由輸出結(jié)果可以看到顶伞,前3個任務(wù)首先執(zhí)行完,然后空閑下來的線程去執(zhí)行第4個任務(wù)剑梳,在FixedThreadPool中唆貌,有一個固定大小的池,如果當(dāng)前需要執(zhí)行的任務(wù)超過了池大小垢乙,那么多余的任務(wù)等待狀態(tài)锨咙,直到有空閑下來的線程執(zhí)行任務(wù),而當(dāng)執(zhí)行的任務(wù)小于池大小追逮,空閑的線程也不會去銷毀酪刀。

  1. CachedThreadPool

上段代碼其它地方不變粹舵,將newFixedThreadPool(3)方法換成newCachedThreadPool()方法。

輸出結(jié)果:

第3次任務(wù)的第1次執(zhí)行  
第4次任務(wù)的第1次執(zhí)行  
第1次任務(wù)的第1次執(zhí)行  
第2次任務(wù)的第1次執(zhí)行  
第4次任務(wù)的第2次執(zhí)行  
第3次任務(wù)的第2次執(zhí)行  
第2次任務(wù)的第2次執(zhí)行  
第1次任務(wù)的第2次執(zhí)行  
第2次任務(wù)的第3次執(zhí)行  
第3次任務(wù)的第3次執(zhí)行  
第1次任務(wù)的第3次執(zhí)行  
第4次任務(wù)的第3次執(zhí)行  
第2次任務(wù)的第4次執(zhí)行  
第4次任務(wù)的第4次執(zhí)行  
第3次任務(wù)的第4次執(zhí)行  
第1次任務(wù)的第4次執(zhí)行  

可見骂倘,4個任務(wù)是交替執(zhí)行的眼滤,CachedThreadPool會創(chuàng)建一個緩存區(qū),將初始化的線程緩存起來历涝,如果線程有可用的诅需,就使用之前創(chuàng)建好的線程,如果沒有可用的荧库,就新創(chuàng)建線程堰塌,終止并且從緩存中移除已有60秒未被使用的線程。

  1. SingleThreadExecutor

上段代碼其它地方不變分衫,將newFixedThreadPool方法換成newSingleThreadExecutor方法场刑。

輸出結(jié)果:

第1次任務(wù)的第1次執(zhí)行  
第1次任務(wù)的第2次執(zhí)行  
第1次任務(wù)的第3次執(zhí)行  
第1次任務(wù)的第4次執(zhí)行  
第2次任務(wù)的第1次執(zhí)行  
第2次任務(wù)的第2次執(zhí)行  
第2次任務(wù)的第3次執(zhí)行  
第2次任務(wù)的第4次執(zhí)行  
第3次任務(wù)的第1次執(zhí)行  
第3次任務(wù)的第2次執(zhí)行  
第3次任務(wù)的第3次執(zhí)行  
第3次任務(wù)的第4次執(zhí)行  
第4次任務(wù)的第1次執(zhí)行  
第4次任務(wù)的第2次執(zhí)行  
第4次任務(wù)的第3次執(zhí)行  
第4次任務(wù)的第4次執(zhí)行  

4個任務(wù)是順序執(zhí)行的,SingleThreadExecutor得到的是一個單個的線程蚪战,這個線程會保證你的任務(wù)執(zhí)行完成牵现,如果當(dāng)前線程意外終止,會創(chuàng)建一個新線程繼續(xù)執(zhí)行任務(wù)屎勘,這和我們直接創(chuàng)建線程不同施籍,也和newFixedThreadPool(1)不同。

4.ScheduledThreadPool

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest {
    public static void main(String[] args) {
        ScheduledExecutorService schedulePool = Executors.newScheduledThreadPool(1);
        // 5秒后執(zhí)行任務(wù)
        schedulePool.schedule(new Runnable() {
            public void run() {
                System.out.println("爆炸");
            }
        }, 5, TimeUnit.SECONDS);
        // 5秒后執(zhí)行任務(wù)概漱,以后每2秒執(zhí)行一次
        schedulePool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("爆炸");
            }
        }, 5, 2, TimeUnit.SECONDS);
    }
}

ScheduledThreadPool可以定時的或延時的執(zhí)行任務(wù)丑慎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瓤摧,隨后出現(xiàn)的幾起案子竿裂,更是在濱河造成了極大的恐慌,老刑警劉巖照弥,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腻异,死亡現(xiàn)場離奇詭異,居然都是意外死亡这揣,警方通過查閱死者的電腦和手機悔常,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來给赞,“玉大人机打,你說我怎么就攤上這事∑福” “怎么了残邀?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我芥挣,道長驱闷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任空免,我火速辦了婚禮空另,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鼓蜒。我一直安慰自己痹换,他們只是感情好征字,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布都弹。 她就那樣靜靜地躺著,像睡著了一般匙姜。 火紅的嫁衣襯著肌膚如雪畅厢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天氮昧,我揣著相機與錄音框杜,去河邊找鬼。 笑死袖肥,一個胖子當(dāng)著我的面吹牛蔫敲,可吹牛的內(nèi)容都是我干的陕见。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼袄膏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乖酬,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤于未,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蒸苇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磷蛹,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年溪烤,在試婚紗的時候發(fā)現(xiàn)自己被綠了味咳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡檬嘀,死狀恐怖槽驶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情枪眉,我是刑警寧澤捺檬,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站贸铜,受9級特大地震影響堡纬,放射性物質(zhì)發(fā)生泄漏聂受。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一烤镐、第九天 我趴在偏房一處隱蔽的房頂上張望蛋济。 院中可真熱鬧,春花似錦炮叶、人聲如沸碗旅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祟辟。三九已至,卻和暖如春侣肄,著一層夾襖步出監(jiān)牢的瞬間旧困,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工稼锅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吼具,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓矩距,卻偏偏與公主長得像拗盒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子锥债,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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