進程:正在執(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類
- 繼承
Thread
類 - 重寫
Thread
的run
方法晾浴。 - 創(chuàng)建子類對象负乡,即線程對象
- 調(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 接口
- 定義類實現(xiàn)
Runnable
接口: 避免繼承Thread類的單繼承局限性 - 覆蓋接口中的
run
方法切省。將線程任務(wù)代碼定義到run
方法中 - 創(chuàng)建
Thread
類的對象最岗,并將Runnable
接口的子類對象作為參數(shù)傳遞給Thread
類的構(gòu)造函數(shù)。因為線程被封裝到Runnable接口的run方法中朝捆,而這個run方法所屬于Runnable接口的子類對象般渡,所以將這個子類對象作為參數(shù)傳遞給THread的構(gòu)造函數(shù)。這樣,線程對象創(chuàng)建時就可以明確要運行的線程任務(wù)驯用。 - 調(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)建線程池孵构。
- 創(chuàng)建一個可重用固定線程集合的線程池,以共享的無界隊列方式來運行這些線程烟很。
ExecutorService threadPool = Executors.newFixedThreadPool(3);// 創(chuàng)建可以容納3個線程的線程池
- 創(chuàng)建一個可根據(jù)需要創(chuàng)建新線程的線程池颈墅,但是在以前構(gòu)造的線程可用時將重用它們。
ExecutorService threadPool = Executors.newCachedThreadPool();// 線程池的大小會根據(jù)執(zhí)行的任務(wù)數(shù)動態(tài)分配
- 創(chuàng)建一個使用單個 worker 線程的 Executor雾袱,以無界隊列方式來運行該線程恤筛。
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 創(chuàng)建單個線程的線程池,如果當(dāng)前線程在執(zhí)行任務(wù)時突然中斷芹橡,則會創(chuàng)建一個新的線程替代它繼續(xù)執(zhí)行任務(wù)
- 創(chuàng)建一個可安排在給定延遲后運行命令或者定期地執(zhí)行的線程池毒坛。
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3); // 效果類似于Timer定時器
每種線程池都有不同的使用場景,下面看一下這四種線程池使用起來有什么不同林说。
- 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ù)小于池大小追逮,空閑的線程也不會去銷毀酪刀。
- 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秒未被使用的線程。
- 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ù)丑慎。