Java 四種線程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

介紹new Thread的弊端及Java四種線程池的使用,對(duì)Android同樣適用。本文是基礎(chǔ)篇,后面會(huì)分享下線程池一些高級(jí)功能。

1、new Thread的弊端

執(zhí)行一個(gè)異步任務(wù)你還只是如下new Thread嗎?

newThread(new Runnable() {

@Overridepublicvoid run() {// TODO Auto-generated method stub}

}).start();

那你就out太多了,new Thread的弊端如下:

a. 每次new Thread新建對(duì)象性能差。

b. 線程缺乏統(tǒng)一管理,可能無(wú)限制新建線程,相互之間競(jìng)爭(zhēng),及可能占用過(guò)多系統(tǒng)資源導(dǎo)致死機(jī)或oom。

c. 缺乏更多功能,如定時(shí)執(zhí)行、定期執(zhí)行、線程中斷。

相比new Thread,Java提供的四種線程池的好處在于:

a. 重用存在的線程,減少對(duì)象創(chuàng)建、消亡的開銷,性能佳。

b. 可有效控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過(guò)多資源競(jìng)爭(zhēng),避免堵塞。

c. 提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。

2、Java 線程池

Java通過(guò)Executors提供四種線程池,分別為:

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í)行。

(1). newCachedThreadPool

創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程。示例代碼如下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

for (int i = 0; i < 10; i++) {

final int index = i;

try {

Thread.sleep(index * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

cachedThreadPool.execute(new Runnable() {

@Override

public void run() {

System.out.println(index);

}

});

}

線程池為無(wú)限大,當(dāng)執(zhí)行第二個(gè)任務(wù)時(shí)第一個(gè)任務(wù)已經(jīng)完成,會(huì)復(fù)用執(zhí)行第一個(gè)任務(wù)的線程,而不用每次新建線程。

(2). newFixedThreadPool

創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。示例代碼如下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

for (int i = 0; i < 10; i++) {

final int index = i;

fixedThreadPool.execute(new Runnable() {

@Override

public void run() {

try {

System.out.println(index);

Thread.sleep(2000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

});

}

因?yàn)榫€程池大小為3,每個(gè)任務(wù)輸出index后sleep 2秒,所以每?jī)擅氪蛴?個(gè)數(shù)字。

定長(zhǎng)線程池的大小最好根據(jù)系統(tǒng)資源進(jìn)行設(shè)置。如Runtime.getRuntime().availableProcessors()。可參考PreloadDataCache

(3) newScheduledThreadPool

創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。延遲執(zhí)行示例代碼如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

scheduledThreadPool.schedule(new Runnable() {

@Override

public void run() {

System.out.println("delay 3 seconds");

}

}, 3, TimeUnit.SECONDS);

表示延遲3秒執(zhí)行。

定期執(zhí)行示例代碼如下:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

@Override

public void run() {

System.out.println("delay 1 seconds, and excute every 3 seconds");

}

}, 1, 3, TimeUnit.SECONDS);

表示延遲1秒后每3秒執(zhí)行一次。

ScheduledExecutorService比Timer更安全,功能更強(qiáng)大,后面會(huì)有一篇單獨(dú)進(jìn)行對(duì)比。

(4)、newSingleThreadExecutor

創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。示例代碼如下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

for (int i = 0; i < 10; i++) {

final int index = i;

singleThreadExecutor.execute(new Runnable() {

@Override

public void run() {

try {

System.out.println(index);

Thread.sleep(2000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

});

}

結(jié)果依次輸出,相當(dāng)于順序執(zhí)行各個(gè)任務(wù)。

現(xiàn)行大多數(shù)GUI程序都是單線程的。Android中單線程可用于數(shù)據(jù)庫(kù)操作,文件操作,應(yīng)用批量安裝,應(yīng)用批量刪除等不適合并發(fā)但可能IO阻塞性及影響UI線程響應(yīng)的操作。

線程池的作用:

線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量。

根 據(jù)系統(tǒng)的環(huán)境情況,可以自動(dòng)或手動(dòng)設(shè)置線程數(shù)量,達(dá)到運(yùn)行的最佳效果;少了浪費(fèi)了系統(tǒng)資源,多了造成系統(tǒng)擁擠效率不高。用線程池控制線程數(shù)量,其他線程排 隊(duì)等候。一個(gè)任務(wù)執(zhí)行完畢,再?gòu)年?duì)列的中取最前面的任務(wù)開始執(zhí)行。若隊(duì)列中沒(méi)有等待進(jìn)程,線程池的這一資源處于等待。當(dāng)一個(gè)新任務(wù)需要運(yùn)行時(shí),如果線程池 中有等待的工作線程,就可以開始運(yùn)行了;否則進(jìn)入等待隊(duì)列。

為什么要用線程池:

1.減少了創(chuàng)建和銷毀線程的次數(shù),每個(gè)工作線程都可以被重復(fù)利用,可執(zhí)行多個(gè)任務(wù)。

2.可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線線程的數(shù)目,防止因?yàn)橄倪^(guò)多的內(nèi)存,而把服務(wù)器累趴下(每個(gè)線程需要大約1MB內(nèi)存,線程開的越多,消耗的內(nèi)存也就越大,最后死機(jī))。

Java里面線程池的頂級(jí)接口是Executor,但是嚴(yán)格意義上講Executor并不是一個(gè)線程池,而只是一個(gè)執(zhí)行線程的工具。真正的線程池接口是ExecutorService。

比較重要的幾個(gè)類:

要配置一個(gè)線程池是比較復(fù)雜的,尤其是對(duì)于線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優(yōu)的,因此在Executors類里面提供了一些靜態(tài)工廠,生成一些常用的線程池。

1. newSingleThreadExecutor

創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來(lái)替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。

2.newFixedThreadPool

創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程。

3. newCachedThreadPool

創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過(guò)了處理任務(wù)所需要的線程,

那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來(lái)處理任務(wù)。此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小。

4.newScheduledThreadPool

創(chuàng)建一個(gè)大小無(wú)限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。

實(shí)例

1:newSingleThreadExecutor·

MyThread.java

public class MyThread extends Thread {

? ? @Override

? ? public void run() {

? ? ? ? System.out.println(Thread.currentThread().getName() + "正在執(zhí)行。。。");

? ? }

}

TestSingleThreadExecutor.java

public class TestSingleThreadExecutor {

? ? public static void main(String[] args) {

? ? ? ? //創(chuàng)建一個(gè)可重用固定線程數(shù)的線程池

? ? ? ? ExecutorService pool = Executors. newSingleThreadExecutor();

? ? ? ? //創(chuàng)建實(shí)現(xiàn)了Runnable接口對(duì)象,Thread對(duì)象當(dāng)然也實(shí)現(xiàn)了Runnable接口

? ? ? ? Thread t1 = new MyThread();

? ? ? ? Thread t2 = new MyThread();

? ? ? ? Thread t3 = new MyThread();

? ? ? ? Thread t4 = new MyThread();

? ? ? ? Thread t5 = new MyThread();

? ? ? ? //將線程放入池中進(jìn)行執(zhí)行

? ? ? ? pool.execute(t1);

? ? ? ? pool.execute(t2);

? ? ? ? pool.execute(t3);

? ? ? ? pool.execute(t4);

? ? ? ? pool.execute(t5);

? ? ? ? //關(guān)閉線程池

? ? ? ? pool.shutdown();

? ? }

}

輸出結(jié)果

pool-1-thread-1正在執(zhí)行。。。

pool-1-thread-1正在執(zhí)行。。。

pool-1-thread-1正在執(zhí)行。。。

pool-1-thread-1正在執(zhí)行。。。

pool-1-thread-1正在執(zhí)行。。。

2:newFixedThreadPool

TestFixedThreadPool.Java

publicclass TestFixedThreadPool {

? ? publicstaticvoid main(String[] args) {

? ? ? ? //創(chuàng)建一個(gè)可重用固定線程數(shù)的線程池

? ? ? ? ExecutorService pool = Executors.newFixedThreadPool(2);

? ? ? ? //創(chuàng)建實(shí)現(xiàn)了Runnable接口對(duì)象,Thread對(duì)象當(dāng)然也實(shí)現(xiàn)了Runnable接口

? ? ? ? Thread t1 = new MyThread();

? ? ? ? Thread t2 = new MyThread();

? ? ? ? Thread t3 = new MyThread();

? ? ? ? Thread t4 = new MyThread();

? ? ? ? Thread t5 = new MyThread();

? ? ? ? //將線程放入池中進(jìn)行執(zhí)行

? ? ? ? pool.execute(t1);

? ? ? ? pool.execute(t2);

? ? ? ? pool.execute(t3);

? ? ? ? pool.execute(t4);

? ? ? ? pool.execute(t5);

? ? ? ? //關(guān)閉線程池

? ? ? ? pool.shutdown();

? ? }

}

輸出結(jié)果

pool-1-thread-1正在執(zhí)行。。。

pool-1-thread-2正在執(zhí)行。。。

pool-1-thread-1正在執(zhí)行。。。

pool-1-thread-2正在執(zhí)行。。。

pool-1-thread-1正在執(zhí)行。。。

3:newCachedThreadPool

TestCachedThreadPool.java

public class TestCachedThreadPool {

? ? public static void main(String[] args) {

? ? ? ? //創(chuàng)建一個(gè)可重用固定線程數(shù)的線程池

? ? ? ? ExecutorService pool = Executors.newCachedThreadPool();

? ? ? ? //創(chuàng)建實(shí)現(xiàn)了Runnable接口對(duì)象,Thread對(duì)象當(dāng)然也實(shí)現(xiàn)了Runnable接口

? ? ? ? Thread t1 = new MyThread();

? ? ? ? Thread t2 = new MyThread();

? ? ? ? Thread t3 = new MyThread();

? ? ? ? Thread t4 = new MyThread();

? ? ? ? Thread t5 = new MyThread();

? ? ? ? //將線程放入池中進(jìn)行執(zhí)行

? ? ? ? pool.execute(t1);

? ? ? ? pool.execute(t2);

? ? ? ? pool.execute(t3);

? ? ? ? pool.execute(t4);

? ? ? ? pool.execute(t5);

? ? ? ? //關(guān)閉線程池

? ? ? ? pool.shutdown();

? ? }

}

輸出結(jié)果

pool-1-thread-2正在執(zhí)行。。。

pool-1-thread-4正在執(zhí)行。。。

pool-1-thread-3正在執(zhí)行。。。

pool-1-thread-1正在執(zhí)行。。。

pool-1-thread-5正在執(zhí)行。。。

4:newScheduledThreadPool

TestScheduledThreadPoolExecutor.java

public class TestScheduledThreadPoolExecutor {

? ? public static void main(String[] args) {

? ? ? ? ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);

? ? ? ? exec.scheduleAtFixedRate(new Runnable() {//每隔一段時(shí)間就觸發(fā)異常

? ? ? ? ? ? ? ? ? ? ? @Override

? ? ? ? ? ? ? ? ? ? ? publicvoid run() {

? ? ? ? ? ? ? ? ? ? ? ? ? //throw new RuntimeException();

? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("================");

? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? }, 1000, 5000, TimeUnit.MILLISECONDS);

? ? ? ? exec.scheduleAtFixedRate(new Runnable() {//每隔一段時(shí)間打印系統(tǒng)時(shí)間,證明兩者是互不影響的

? ? ? ? ? ? ? ? ? ? ? @Override

? ? ? ? ? ? ? ? ? ? ? publicvoid run() {

? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println(System.nanoTime());

? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? }, 1000, 2000, TimeUnit.MILLISECONDS);

? ? }

}

輸出結(jié)果:

================

8384644549516

8386643829034

8388643830710

================

8390643851383

8392643879319

8400643939383

本文摘自:https://www.cnblogs.com/zhujiabin/p/5404771.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 1、new Thread的弊端 執(zhí)行一個(gè)異步任務(wù)你還只是如下new Thread嗎? ``` new Thread...
    Jannonx閱讀 2,771評(píng)論 0 6
  • 第一部分 來(lái)看一下線程池的框架圖,如下: 1、Executor任務(wù)提交接口與Executors工具類 Execut...
    壓抑的內(nèi)心閱讀 4,291評(píng)論 1 24
  • 前段時(shí)間遇到這樣一個(gè)問(wèn)題,有人問(wèn)微信朋友圈的上傳圖片的功能怎么做才能讓用戶的等待時(shí)間較短,比如說(shuō)一下上傳9張圖片,...
    加油碼農(nóng)閱讀 1,217評(píng)論 0 2
  • 序言 近日后臺(tái)需要一些數(shù)據(jù),需要從網(wǎng)上爬取,但是爬取的過(guò)程中,由于訪問(wèn)速度太頻繁,造成IP被封,最終通過(guò)線程池解決...
    HusterYP閱讀 858評(píng)論 0 3
  • 第一次知道一個(gè)人有秘密,可以對(duì)著洞訴說(shuō)是在電影《花樣年華》里。 一個(gè)人很沉靜的在那里絮絮叨叨,過(guò)后一...
    清凡絢影閱讀 286評(píng)論 0 1