1.引言
最近的工作大量運(yùn)用了多線程的一些知識(shí),才發(fā)現(xiàn)自己對(duì)這塊知識(shí)有很多盲點(diǎn),于是看看書(shū),博客,寫(xiě)寫(xiě),總結(jié)總結(jié)。
2.正題
1.線程的狀態(tài)
- 新建
剛創(chuàng)建的時(shí)候的狀態(tài) - 就緒
等待獲取cpu時(shí)間片 - 阻塞
阻塞狀態(tài)的線程等待一個(gè)監(jiān)視器鎖以進(jìn)入一個(gè)同步代碼塊/同步方法,或者該線程在調(diào)用wait()方法后重如該同步代碼塊/同步方法 - 運(yùn)行
- 死亡
1.java Thread.yield 方法
yield方法 讓出當(dāng)前Thread的cpu,使當(dāng)前線程變成就緒態(tài),和其他線程一起競(jìng)爭(zhēng)cpu資源。yield方法并沒(méi)有終止當(dāng)前線程的意思。
2.線程池shutdown 和shutdownNow
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException {@inheritDoc}
*/
shutdown方法本質(zhì)調(diào)用線程池各個(gè)線程的interrupt()方法,調(diào)用這個(gè)方法之后,線程池將不再接受新的任務(wù),調(diào)用這個(gè)方法之后,再submit一個(gè)任務(wù)就會(huì)報(bào)RejectedExecutionException異常
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution. These tasks are drained (removed)
* from the task queue upon return from this method.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
* do that.
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
* cancels tasks via {@link Thread#interrupt}, so any task that
* fails to respond to interrupts may never terminate.
*
* @throws SecurityException {@inheritDoc}
*/
shutdownNow和shutdown方法一樣,唯一不同,shutdownNow會(huì)盡最大的可能去終止正在運(yùn)行的任務(wù),并且返回沒(méi)有執(zhí)行的Runnable
3.Callable vs Runnable 接口
Runnable 執(zhí)行完畢沒(méi)有返回值
Callable執(zhí)行完畢有返回值,通過(guò)Feture.get()獲取返回值,此方法會(huì)使線程進(jìn)入阻塞態(tài)
public static void main(String[] args) {
ThreadPoolExecutor m = (ThreadPoolExecutor) Executors.newCachedThreadPool();
Future <String>f=m.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(8000);
return "wxy";
}
});
System.out.println("開(kāi)始阻塞");
try {
String s=f.get();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("結(jié)束");
}
4.線程優(yōu)先級(jí)
線程的等級(jí)在1-10之間,小于1 或者大于10 將報(bào)異常。優(yōu)先級(jí)越高,獲取cpu時(shí)間片越多,獲取的幾率也越大。通過(guò)setPriority方法設(shè)置
5.后臺(tái)線程
后臺(tái)線程,它是在后臺(tái)運(yùn)行的,它的任務(wù)是為其他線程提供服務(wù),這種線程被稱為“后臺(tái)線程(Daemon Thread)”,又稱為“守護(hù)線程”或“精靈線程”。JVM的垃圾回收線程就是典型的后臺(tái)線程。當(dāng)所有的用戶線程(非守護(hù)線程)
當(dāng)所有的前臺(tái)線程死亡了,后臺(tái)線程也會(huì)自動(dòng)消亡,注意的是前臺(tái)線程死亡,不等于睡眠
6.thread.join()
在線程a中調(diào)用線程b的join()方法,會(huì)中斷線程a的執(zhí)行,等待線程b執(zhí)行完畢才會(huì)在繼續(xù)執(zhí)行線程a。
public static void main(String[] args) {
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 10; i <20 ; i++) {
System.out.println(i);
}
}
});
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println(i);
if (i==5){
try {
thread2.start();
thread2.join();//放在start后面
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
thread1.start();
}
輸出:
1 2 3 4 5 10 11 12 13 14 15 16 17 18 19 6 7 8 9
7.java Lock對(duì)象
synchronized 修飾的代碼塊或者方法,處于阻塞狀態(tài)的時(shí)候,那么其他線程都無(wú)法訪問(wèn)這個(gè)方法,會(huì)一直阻塞下去。為了解決這種現(xiàn)象于是出現(xiàn)了Lock。Lock是一個(gè)接口,實(shí)現(xiàn)類(lèi)是ReentrantLock ,ReentrantLock是一個(gè)可重入且獨(dú)占式的鎖,它具有與使用synchronized監(jiān)視器鎖相同的基本行為和語(yǔ)義,但與synchronized關(guān)鍵字相比,它更靈活、更強(qiáng)大,增加了輪詢、超時(shí)、中斷等高級(jí)功能。ReentrantLock,顧名思義,它是支持可重入鎖的鎖,是一種遞歸無(wú)阻塞的同步機(jī)制。除此之外,該鎖還支持獲取鎖時(shí)的公平和非公平選擇
8.volatile 關(guān)鍵字
volatile 關(guān)鍵字只能保證變量的可見(jiàn)性,在線程a中,被volatile修飾的變量a值改變,a將會(huì)通知其他所有線程從主存再次獲取a的值,這樣a改變了,那么其他線程在使用a的值的時(shí)候,是最新的值,而不是緩存下來(lái)的值
9.ThreadLocal
ThreadLocal 本地?cái)?shù)據(jù)接口,以當(dāng)前Thread 為key,value是我們set的值。相當(dāng)于為每個(gè)thread保存一個(gè)副本。這樣下次這個(gè)線程訪問(wèn)的就是自己保存的副本,保證了數(shù)據(jù)的唯一性,從根源上解決多線程問(wèn)題
10.Thread.interrupted vs thread.interrupt

這倆個(gè)方法都是用來(lái)獲取前程是否中斷的,唯一的不同點(diǎn)就是thread.interrupt不會(huì)重置狀態(tài)。Thread.interrupted會(huì)重置狀態(tài)(重置成false)。所有線程默認(rèn)是非中斷 的。重置的意思是:當(dāng)一個(gè)線程設(shè)置成中斷,第一次調(diào)用Thread.interrupted 將返回true,第二次調(diào)用將返回false。
線程池關(guān)閉某一個(gè)線程java實(shí)現(xiàn):
public static void main(String[] args) {
ThreadPoolExecutor mThreadPoolExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
long t=System.currentTimeMillis();
Future mFuture = mThreadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
while (Thread.interrupted() == false) {
System.out.println("while正在執(zhí)行");
}
System.out.println("當(dāng)前線程是否中斷:"+Thread.interrupted());//最后打印的為false,清除了之前的狀態(tài)
}
});
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mFuture.cancel(true);
System.out.println(System.currentTimeMillis()-t);
}
11 線程wait 和notify/notifyall
對(duì)象.wait(): 導(dǎo)致當(dāng)前的線程等待,直到其他線程調(diào)用此對(duì)象的notify( ) 方法或 notifyAll( ) 方法
對(duì)象.notify():喚醒在此對(duì)象鎖上等待的單個(gè)線程
public static void main(String[] args) {
Object m=new Object();
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <20; i++) {
if (i==10){
synchronized (m){
try {
m.wait();//假如不要m,直接調(diào)用就會(huì)報(bào)異常
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else {
System.out.println(i);
}
}
}
});
thread1.start();
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("喚醒線程");
synchronized (m){
m.notify();
}
}
注意點(diǎn):
含有wait 和notify 代碼塊都需要用synchronized 修飾。并且還要一樣。
12 Thread.sleep 和 Object.wait 區(qū)別
在一個(gè)加鎖了的代碼塊中執(zhí)行Thread.sleep,不會(huì)釋放鎖。
在一個(gè)加了鎖的代碼塊中執(zhí)行wait,會(huì)釋放鎖