概念:
- 進(jìn)程: 每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(進(jìn)程上下文)变骡,進(jìn)程間的切換會(huì)有較大的開(kāi)銷(xiāo),一個(gè)進(jìn)程包含1--n個(gè)線程芭逝。
- 線程: 進(jìn)程中負(fù)責(zé)程序執(zhí)行的執(zhí)行單元塌碌。一個(gè)進(jìn)程中至少有一個(gè)線程。
- 多線程:解決多任務(wù)同時(shí)執(zhí)行的需求铝耻,合理使用CPU資源誊爹。多線程的運(yùn)行是根據(jù)CPU切換完成,如何切換由CPU決定瓢捉,因此多線程運(yùn)行具有不確定性频丘。
- 線程池:基本思想還是一種對(duì)象池的思想,開(kāi)辟一塊內(nèi)存空間泡态,里面存放了眾多(未死亡)的線程搂漠,池中線程執(zhí)行調(diào)度由池管理器來(lái)處理。當(dāng)有線程任務(wù)時(shí)某弦,從池中取一個(gè)桐汤,執(zhí)行完成后線程對(duì)象歸池而克,這樣可以避免反復(fù)創(chuàng)建線程對(duì)象所帶來(lái)的性能開(kāi)銷(xiāo),節(jié)省了系統(tǒng)的資源怔毛。
創(chuàng)建線程的兩種方式
- 繼承Thread類(lèi)员萍,覆蓋run()方法。創(chuàng)建線程對(duì)象并用start()方法啟動(dòng)線程拣度。
- 實(shí)現(xiàn)Runnable接口
Thread 類(lèi)中的 start() 和 run() 方法的區(qū)別
調(diào)用 start() 方法才會(huì)啟動(dòng)新線程碎绎;如果直接調(diào)用 Thread 的 run() 方法抗果,它的行為就會(huì)和普通的方法一樣筋帖;為了在新的線程中執(zhí)行我們的代碼日麸,必須使用 Thread.start() 方法。
用 Runnable 還是 Thread
我們都知道可以通過(guò)繼承 Thread 類(lèi)或者調(diào)用 Runnable 接口來(lái)實(shí)現(xiàn)線程蚣旱,問(wèn)題是碑幅,創(chuàng)建線程哪種方式更好呢?什么情況下使用它塞绿?這個(gè)問(wèn)題很容易回答沟涨,如果你知道Java不支持類(lèi)的多重繼承,但允許你調(diào)用多個(gè)接口异吻。所以如果你要繼承其他類(lèi)裹赴,當(dāng)然是調(diào)用Runnable接口更好了
Android 系統(tǒng)接口 HandlerThread 繼承了 Thread,它是一個(gè)可以使用 Handler 的 Thread诀浪,一個(gè)具有消息循環(huán)的線程棋返。run()方法中通過(guò) Looper.prepare() 來(lái)創(chuàng)建消息隊(duì)列,通過(guò) Looper.loop() 來(lái)開(kāi)啟消息循環(huán)雷猪【ⅲ可以在 run() 方法中執(zhí)行耗時(shí)的任務(wù),而 HandlerThread 內(nèi)部創(chuàng)建了消息隊(duì)列外界需要通過(guò) Handler 的方式來(lái)通知 HandlerThread 執(zhí)行一個(gè)具體任務(wù)求摇;HandlerThread 的 run() 方法是一個(gè)無(wú)限的循環(huán)射沟,可以通過(guò)它的 quite() 或 quitSafely() 方法來(lái)終止線程的執(zhí)行殊者;
創(chuàng)建線程的第三種方式
Callable 是在 JDK1.5 增加的(Runnable在JDK1.0)。 與Runnable的主要區(qū)別是 Callable 的 call() 方法可以返回值和拋出異常验夯,而 Runnable 的 run() 方法沒(méi)有這些功能猖吴。Callable 可以返回裝載有計(jì)算結(jié)果的 Future 對(duì)象。
源碼:
public interface Runnable {
public void run();
}
public interface Callable<V> {
V call() throws Exception;
}
Callable無(wú)法在新線程中new Thread(Runnable r)使用挥转,Thread 類(lèi)只支持 Runnable距误。不過(guò) Callable 可以使用 ExecutorService 。
#######Future
該接口定義5個(gè)方法
boolean cancel(boolean mayInterruptIfRunning):試圖取消對(duì)此任務(wù)的執(zhí)行扁位。如果任務(wù)已完成准潭、或已取消,或者由于某些其他原因而無(wú)法取消域仇,則此嘗試將失敗刑然。當(dāng)調(diào)用 cancel() 時(shí),如果調(diào)用成功暇务,而此任務(wù)尚未啟動(dòng)泼掠,則此任務(wù)將永不運(yùn)行。如果任務(wù)已經(jīng)啟動(dòng)垦细,則 mayInterruptIfRunning 參數(shù)確定是否應(yīng)該以試圖停止任務(wù)的方式來(lái)中斷執(zhí)行此任務(wù)的線程择镇。此方法返回后,對(duì) isDone() 的后續(xù)調(diào)用將始終返回 true括改。如果此方法返回 true腻豌,則對(duì) isCancelled() 的后續(xù)調(diào)用將始終返回 true。
boolean isCancelled():如果在任務(wù)正常完成前將其取消嘱能,則返回 true吝梅。
boolean isDone():如果任務(wù)已完成,則返回 true惹骂。 可能由于正常終止苏携、異常或取消而完成对粪,在所有這些情況中右冻,此方法都將返回 true。
V get()throws InterruptedException,ExecutionException:如有必要著拭,等待計(jì)算完成纱扭,然后獲取其結(jié)果。
V get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException: 如有必要茫死,最多等待為使計(jì)算完成所給定的時(shí)間之后跪但,獲取其結(jié)果(如果結(jié)果可用)
#######FutureTask
FutureTask實(shí)現(xiàn)了兩個(gè)接口,Runnable和Future,所以它既可以作為Runnable被線程執(zhí)行屡久,又可以作為Future得到Callable的返回值忆首。
public class FutureTask<V> implements RunnableFuture<V> {
...
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
示例
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "哈哈哈";
}
};
FutureTask<String> task = new FutureTask<String>(callable);
Thread t = new Thread(task);
t.start(); // 啟動(dòng)線程
task.cancel(true); // 取消線程
多線程
使用多線程得好處:
優(yōu)點(diǎn):
1)適當(dāng)?shù)奶岣叱绦虻膱?zhí)行效率(多個(gè)線程同時(shí)執(zhí)行)。
2)適當(dāng)?shù)奶岣吡速Y源利用率(CPU被环、內(nèi)存等)糙及。
缺點(diǎn):
1)占用一定的內(nèi)存空間。
2)線程越多CPU的調(diào)度開(kāi)銷(xiāo)越大筛欢。
3)程序的復(fù)雜度會(huì)上升浸锨。
要點(diǎn)
synchronized
synchronized是Java中的關(guān)鍵字,是一種同步鎖版姑。它修飾的對(duì)象有以下幾種:
修飾一個(gè)代碼塊柱搜,被修飾的代碼塊稱為同步語(yǔ)句塊,其作用的范圍是大括號(hào){}括起來(lái)的代碼剥险,作用的對(duì)象是調(diào)用這個(gè)代碼塊的對(duì)象聪蘸;
修飾一個(gè)方法,被修飾的方法稱為同步方法表制,其作用的范圍是整個(gè)方法健爬,作用的對(duì)象是調(diào)用這個(gè)方法的對(duì)象;
修改一個(gè)靜態(tài)的方法么介,其作用的范圍是整個(gè)靜態(tài)方法娜遵,作用的對(duì)象是這個(gè)類(lèi)的所有對(duì)象;
修改一個(gè)類(lèi)壤短,其作用的范圍是synchronized后面括號(hào)括起來(lái)的部分设拟,作用主的對(duì)象是這個(gè)類(lèi)的所有對(duì)象。
wait()鸽扁、notify()蒜绽、notifyAll()
wait():
導(dǎo)致線程進(jìn)入等待狀態(tài),直到它被其他線程通過(guò)notify()或者notifyAll喚醒桶现,該方法只能在同步方法中調(diào)用。
notify():
隨機(jī)選擇一個(gè)在該對(duì)象上調(diào)用wait方法的線程鼎姊,解除其阻塞狀態(tài)骡和,該方法只能在同步方法或同步塊內(nèi)部調(diào)用。
notifyAll():
解除所有那些在該對(duì)象上調(diào)用wait方法的線程的阻塞狀態(tài)相寇,同樣該方法只能在同步方法或同步塊內(nèi)部調(diào)用慰于。
調(diào)用這三個(gè)方法中任意一個(gè),當(dāng)前線程必須是鎖的持有者唤衫,如果不是會(huì)拋出一個(gè) IllegalMonitorStateException 異常婆赠。
wait() 與 Thread.sleep(long time) 的區(qū)別
sleep():在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)拖叙。
wait() 方法使實(shí)體所處線程暫停執(zhí)行,從而使對(duì)象進(jìn)入等待狀態(tài)剃盾,直到被 notify() 方法通知或者 wait() 的等待的時(shí)間到坏逢。
sleep() 方法使持有的線程暫停運(yùn)行,從而使線程進(jìn)入休眠狀態(tài)妙黍,直到用 interrupt 方法來(lái)打斷他的休眠或者 sleep 的休眠的時(shí)間到悴侵。
wait() 方法進(jìn)入等待狀態(tài)時(shí)會(huì)釋放同步鎖,而 sleep() 方法不會(huì)釋放同步鎖拭嫁。
join()
thread.Join把指定的線程加入到當(dāng)前線程可免,可以將兩個(gè)交替執(zhí)行的線程合并為順序執(zhí)行的線程。比如在線程B中調(diào)用了線程A的Join()方法做粤,直到線程A執(zhí)行完畢后浇借,才會(huì)繼續(xù)執(zhí)行線程B。
t.join(); //使調(diào)用線程 t 在此之前執(zhí)行完畢怕品。
t.join(1000); //等待 t 線程逮刨,等待時(shí)間是1000毫秒
偽代碼
Thread t1 = new Thread(線程一);
Thread t2 = new Thread(線程二);
t1.start();
t1.join(); // 等待線程一執(zhí)行完成,再執(zhí)行線程二
t2.start();
啟動(dòng) t1 后堵泽,調(diào)用了 join() 方法修己,直到 t1 的任務(wù)結(jié)束,才輪到 t2 啟動(dòng)迎罗,然后 t2 才開(kāi)始任務(wù)睬愤,兩個(gè)線程是按著嚴(yán)格的順序來(lái)執(zhí)行的。
**Thread.yield() **
Thread.yield():線程放棄運(yùn)行纹安,將CPU的控制權(quán)讓出尤辱。
yield() 方法讓出控制權(quán)后,如果優(yōu)先級(jí)高于其他的線程厢岂,還有可能馬上被系統(tǒng)的調(diào)度機(jī)制選中來(lái)運(yùn)行光督。
線程池
優(yōu)點(diǎn):
1)避免線程的創(chuàng)建和銷(xiāo)毀帶來(lái)的性能開(kāi)銷(xiāo)。
2)避免大量的線程間因互相搶占系統(tǒng)資源導(dǎo)致的阻塞現(xiàn)象塔粒。
3}能夠?qū)€程進(jìn)行簡(jiǎn)單的管理并提供定時(shí)執(zhí)行结借、間隔執(zhí)行等功能。
Java里面線程池的頂級(jí)接口是 Executor卒茬,不過(guò)真正的線程池接口是 ExecutorService船老, ExecutorService 的默認(rèn)實(shí)現(xiàn)是 ThreadPoolExecutor;
普通類(lèi) Executors 里面調(diào)用的就是 ThreadPoolExecutor圃酵。
Java通過(guò)Executors提供四種線程池
newCachedThreadPool 可變尺寸的線程池柳畔,但是在以前構(gòu)造的線程可用時(shí)將重用它們。對(duì)于執(zhí)行很多短期異步任務(wù)的程序而言郭赐,這些線程池通承胶可提高程序性能。調(diào)用 execute() 將重用以前構(gòu)造的線程(如果線程可用)。如果現(xiàn)有線程沒(méi)有可用的俘陷,則創(chuàng)建一個(gè)新線程并添加到池中罗捎。終止并從緩存中移除那些已有 60 秒鐘未被使用的線程。因此岭洲,長(zhǎng)時(shí)間保持空閑的線程池不會(huì)使用任何資源宛逗。注意,可以使用 ThreadPoolExecutor 構(gòu)造方法創(chuàng)建具有類(lèi)似屬性但細(xì)節(jié)不同(例如超時(shí)參數(shù))的線程池盾剩。(只有非核心線程雷激,最大線程數(shù)非常大,所有線程都活動(dòng)時(shí)告私,會(huì)為新任務(wù)創(chuàng)建新線程屎暇,否則利用空閑線程(60s空閑時(shí)間,過(guò)了就會(huì)被回收驻粟,所以線程池中有0個(gè)線程的可能)處理任務(wù))
newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池根悼,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),所有的任務(wù)是串行執(zhí)行的蜀撑,如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束挤巡,那么會(huì)有一個(gè)新的線程來(lái)替代它,保證所有任務(wù)按照指定順序(FIFO)執(zhí)行酷麦。(只有一個(gè)核心線程矿卑,確保所有任務(wù)都在同一線程中按順序完成。因此不需要處理線程同步的問(wèn)題沃饶。)
newFixedThreadPool 固定大小的線程池母廷,每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小糊肤,線程池的大小一旦達(dá)到最大值就會(huì)保持不變琴昆,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程馆揉。(只有核心線程业舍,并且數(shù)量固定的,也不會(huì)被回收把介,所有線程都活動(dòng)時(shí)勤讽,因?yàn)殛?duì)列沒(méi)有限制大小,新任務(wù)會(huì)等待執(zhí)行拗踢。)
newScheduledThreadPool 創(chuàng)建一個(gè)大小無(wú)限的線程池,此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求向臀。
ThreadPoolExecutor是Executors類(lèi)的底層實(shí)現(xiàn)巢墅。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
1)corePoolSize:線程池的核心線程數(shù),一般情況下不管有沒(méi)有任務(wù)都會(huì)一直在線程池中一直存活,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true 時(shí)君纫,閑置的核心線程會(huì)存在超時(shí)機(jī)制驯遇,如果在指定時(shí)間沒(méi)有新任務(wù)來(lái)時(shí),核心線程也會(huì)被終止蓄髓,而這個(gè)時(shí)間間隔由第3個(gè)屬性 keepAliveTime 指定叉庐。
2)maximumPoolSize:線程池所能容納的最大線程數(shù),當(dāng)活動(dòng)的線程數(shù)達(dá)到這個(gè)值后会喝,后續(xù)的新任務(wù)將會(huì)被阻塞陡叠。
3)keepAliveTime:控制線程閑置時(shí)的超時(shí)時(shí)長(zhǎng),超過(guò)則終止該線程肢执。一般情況下用于非核心線程枉阵,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true時(shí),也作用于核心線程预茄。
4)unit:用于指定 keepAliveTime 參數(shù)的時(shí)間單位兴溜,TimeUnit 是個(gè) enum 枚舉類(lèi)型,常用的有:TimeUnit.HOURS(小時(shí))耻陕、TimeUnit.MINUTES(分鐘)拙徽、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等。
5)workQueue:線程池的任務(wù)隊(duì)列诗宣,通過(guò)線程池的 execute(Runnable command) 方法會(huì)將任務(wù) Runnable 存儲(chǔ)在隊(duì)列中膘怕。
6)threadFactory:線程工廠,它是一個(gè)接口梧田,用來(lái)為線程池創(chuàng)建新線程的淳蔼。
-
ThreadPoolExecutor執(zhí)行任務(wù)時(shí)的心路歷程(以下用currentSize表示線程池中當(dāng)前線程數(shù)量):
(1)當(dāng)currentSize<corePoolSize時(shí),沒(méi)什么好說(shuō)的裁眯,直接啟動(dòng)一個(gè)核心線程并執(zhí)行任務(wù)鹉梨。
(2)當(dāng)currentSize>=corePoolSize、并且workQueue未滿時(shí)穿稳,添加進(jìn)來(lái)的任務(wù)會(huì)被安排到workQueue中等待執(zhí)行存皂。
(3)當(dāng)workQueue已滿,但是currentSize<maximumPoolSize時(shí)逢艘,會(huì)立即開(kāi)啟一個(gè)非核心線程來(lái)執(zhí)行任務(wù)旦袋。
(4)當(dāng)currentSize>=corePoolSize、workQueue已滿它改、并且currentSize>maximumPoolSize時(shí)疤孕,調(diào)用handler默認(rèn)拋出RejectExecutionExpection異常。
線程池 submit 和 execute
接收的參數(shù)不一樣
submit有返回值央拖,而execute沒(méi)有
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
void execute(Runnable command);
實(shí)現(xiàn):submit內(nèi)部調(diào)用execute祭阀,有返回值鹉戚,拋出異常。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
線程池的關(guān)閉
ThreadPoolExecutor 提供了兩個(gè)方法专控,用于線程池的關(guān)閉抹凳。
shutdown():不會(huì)立即的終止線程池,而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止伦腐,但再也不會(huì)接受新的任務(wù)赢底。
shutdownNow():立即終止線程池,并嘗試打斷正在執(zhí)行的任務(wù)柏蘑,并且清空任務(wù)緩存隊(duì)列幸冻,返回尚未執(zhí)行的任務(wù)。