1.并發(fā)和并行有什么區(qū)別劫乱?
(來源——實(shí)戰(zhàn)Java高并發(fā)程序設(shè)計(jì))
并發(fā)偏重于多個(gè)任務(wù)交替執(zhí)行膊畴,而多個(gè)任務(wù)之間有可能還是串行的掘猿;而并行是真正意義上的同時(shí)執(zhí)行。
從嚴(yán)格意義.上來說唇跨,并行的多個(gè)任務(wù)是真的同時(shí)執(zhí)行稠通,而對(duì)于并發(fā)來說,這個(gè)過程只是交替的买猖,一會(huì)兒執(zhí)行任務(wù)A改橘,一會(huì)兒執(zhí)行任務(wù)B,系統(tǒng)會(huì)不停地在兩者之間切換。
實(shí)際上玉控,如果系統(tǒng)內(nèi)只有一個(gè)CPU,而使用多進(jìn)程或者多線程任務(wù)飞主,那么真實(shí)環(huán)境中這些任務(wù)不可能是真實(shí)并行的,畢竟一個(gè)CPU 一次只能執(zhí)行一條指令高诺,在這種情況下多進(jìn)程或者多線程就是并發(fā)的碌识,而不是并行的(操作系統(tǒng)會(huì)不停地切換多個(gè)任務(wù))。真實(shí)的并行也只可能出現(xiàn)在擁有多個(gè)CPU的系統(tǒng)中(比如多核CPU)虱而。.
2.線程和進(jìn)程的區(qū)別筏餐?
做個(gè)簡(jiǎn)單的比喻:進(jìn)程=火車,線程=車廂
線程在進(jìn)程下行進(jìn)(單純的車廂無法運(yùn)行)
一個(gè)進(jìn)程可以包含多個(gè)線程(一輛火車可以有多個(gè)車廂)
不同進(jìn)程間數(shù)據(jù)很難共享(一輛火車上的乘客很難換到另外一輛火車牡拇,比如站點(diǎn)換乘)
同一進(jìn)程下不同線程間數(shù)據(jù)很易共享(A車廂換到B車廂很容易)
進(jìn)程要比線程消耗更多的計(jì)算機(jī)資源(采用多列火車相比多個(gè)車廂更耗資源)
進(jìn)程間不會(huì)相互影響魁瞪,一個(gè)線程掛掉將導(dǎo)致整個(gè)進(jìn)程掛掉(一列火車不會(huì)影響到另外一列火車,但是如果一列火車上中間的一節(jié)車廂著火了惠呼,將影響到所有車廂)
進(jìn)程可以拓展到多機(jī)导俘,進(jìn)程最多適合多核(不同火車可以開在多個(gè)軌道上,同一火車的車廂不能在行進(jìn)的不同的軌道上)
進(jìn)程使用的內(nèi)存地址可以上鎖剔蹋,即一個(gè)線程使用某些共享內(nèi)存時(shí)旅薄,其他線程必須等它結(jié)束,才能使用這一塊內(nèi)存滩租。(比如火車上的洗手間)-"互斥鎖"
進(jìn)程使用的內(nèi)存地址可以限定使用量(比如火車上的餐廳赋秀,最多只允許多少人進(jìn)入,如果滿了需要在門口等律想,等有人出來了才能進(jìn)去)-“信號(hào)量”
轉(zhuǎn)自:https://www.zhihu.com/question/25532384/answer/411179772
3.守護(hù)線程是什么猎莲?
守護(hù)線程(daemon thread),是個(gè)服務(wù)線程技即,準(zhǔn)確地來說就是服務(wù)其他的線程著洼,類似垃圾回收線程。
4.創(chuàng)建線程有哪幾種方式而叼?
一身笤、繼承Thread類創(chuàng)建線程類
(1)繼承Thread類,并重寫run方法葵陵,該run方法的方法體就代表了線程要完成的任務(wù)液荸。因此把run()方法稱為執(zhí)行體。
(2)創(chuàng)建Thread子類的實(shí)例脱篙,即創(chuàng)建了線程對(duì)象娇钱。
(3)調(diào)用線程對(duì)象的start()方法來啟動(dòng)該線程。
package com.example.demo.thread;
public class MyThread extends Thread {
int i = 0;
@Override
public void run() {
for (; i < 100; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
if (i == 50) {
new MyThread().start();
new MyThread().start();
}
}
}
}
上述代碼中Thread.currentThread()方法返回當(dāng)前正在執(zhí)行的線程對(duì)象绊困。getName()方法返回調(diào)用該方法的線程的名字文搂。
二、通過Runnable接口創(chuàng)建線程類
(1)定義runnable接口的實(shí)現(xiàn)類秤朗,并重寫該接口的run()方法煤蹭,該run()方法的方法體同樣是該線程的線程執(zhí)行體。
(2)創(chuàng)建 Runnable實(shí)現(xiàn)類的實(shí)例取视,并依此實(shí)例作為Thread的target來創(chuàng)建Thread對(duì)象硝皂,該Thread對(duì)象才是真正的線程對(duì)象。
(3)調(diào)用線程對(duì)象的start()方法來啟動(dòng)該線程贫途。
package com.example.demo.thread;
public class MyRunnable implements Runnable {
private int i;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
MyRunnable runnable = new MyRunnable();
new Thread(runnable, "新線程1").start();
new Thread(runnable, "新線程2").start();
}
}
}
}
三吧彪、通過Callable和Future創(chuàng)建線程
(1)創(chuàng)建Callable接口的實(shí)現(xiàn)類,并實(shí)現(xiàn)call()方法丢早,該call()方法將作為線程執(zhí)行體姨裸,并且有返回值。
(2)創(chuàng)建Callable實(shí)現(xiàn)類的實(shí)例怨酝,使用FutureTask類來包裝Callable對(duì)象傀缩,該FutureTask對(duì)象封裝了該Callable對(duì)象的call()方法的返回值。
(3)使用FutureTask對(duì)象作為Thread對(duì)象的target創(chuàng)建并啟動(dòng)新線程农猬。
(4)調(diào)用FutureTask對(duì)象的get()方法來獲得子線程執(zhí)行結(jié)束后的返回值
package com.example.demo.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable<Integer> {
public static void main(String[] args) {
MyCallable callable = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(callable);
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 的循環(huán)變量i的值" + i);
if (i == 20) {
new Thread(ft, "有返回值的線程").start();
}
}
try {
System.out.println("子線程的返回值:" + ft.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception {
int i = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
return i;
}
}
采用實(shí)現(xiàn)Runnable赡艰、Callable接口的方式創(chuàng)見多線程時(shí),優(yōu)勢(shì)是:
線程類只是實(shí)現(xiàn)了Runnable接口或Callable接口斤葱,還可以繼承其他類慷垮。
在這種方式下揖闸,多個(gè)線程可以共享同一個(gè)target對(duì)象,所以非常適合多個(gè)相同線程來處理同一份資源的情況料身,從而可以將CPU汤纸、代碼和數(shù)據(jù)分開,形成清晰的模型芹血,較好地體現(xiàn)了面向?qū)ο蟮乃枷搿?br>
劣勢(shì)是:
編程稍微復(fù)雜贮泞,如果要訪問當(dāng)前線程,則必須使用Thread.currentThread()方法幔烛。
使用繼承Thread類的方式創(chuàng)建多線程時(shí)優(yōu)勢(shì)是:
編寫簡(jiǎn)單啃擦,如果需要訪問當(dāng)前線程,則無需使用Thread.currentThread()方法饿悬,直接使用this即可獲得當(dāng)前線程令蛉。
劣勢(shì)是:
線程類已經(jīng)繼承了Thread類,所以不能再繼承其他父類乡恕。
5.說一下 runnable 和 callable 有什么區(qū)別言询?
1)Runnable提供run方法,不會(huì)拋出異常傲宜,只能在run方法內(nèi)部處理異常运杭。Callable提供call方法,直接拋出Exception異常函卒,也就是你不會(huì)因?yàn)閏all方法內(nèi)部出現(xiàn)檢查型異常而不知所措辆憔,完全可以拋出即可。
2)Runnable的run方法無返回值报嵌,Callable的call方法提供返回值用來表示任務(wù)運(yùn)行的結(jié)果
3)Runnable可以作為Thread構(gòu)造器的參數(shù)虱咧,通過開啟新的線程來執(zhí)行,也可以通過線程池來執(zhí)行锚国。而Callable只能通過線程池執(zhí)行腕巡。
6.線程的生命周期?
- 新建 (New) :當(dāng)程序使用 new 關(guān)鍵字創(chuàng)建了一個(gè)線程后血筑。此時(shí)由 JVM 為其分配內(nèi)存绘沉,并初始化其成員變量的值。
- 可運(yùn)行/就緒(Runnable):當(dāng)調(diào)用了 start()方法豺总,線程就處于可運(yùn)行狀態(tài)车伞。Java 虛擬機(jī)會(huì)為其創(chuàng)建方法調(diào)用棧可程序計(jì)數(shù)器喻喳,等到調(diào)度運(yùn)行另玖。
- 運(yùn)行狀態(tài)(Running):處于可運(yùn)行狀態(tài)的線程獲得 CPU 分片后,執(zhí)行 run()方法。
- 阻塞(Blocked):當(dāng)處于運(yùn)行狀態(tài)的線程失去了所占有的資源谦去。
- 死亡(Dead):程序正常完成慷丽、或者線程拋出一個(gè)未捕獲的 Exception 貨或 Error■蓿或者調(diào)用線程的中斷方法盈魁。
不要試圖對(duì)一個(gè)已經(jīng)死亡的線程調(diào)用 start() 方法讓它重新啟動(dòng),死亡就是死亡了窃诉,該線程不可再次作為線程執(zhí)行。
7.sleep() 和 wait() 有什么區(qū)別赤套?
sleep是Thread類的方法,wait是Object類中定義的方法
1飘痛、sleep就是正在執(zhí)行的線程主動(dòng)讓出cpu,cpu去執(zhí)行其他線程容握,在sleep指定的時(shí)間過后宣脉,cpu才會(huì)回到這個(gè)線程上繼續(xù)往下執(zhí)行
2、如果當(dāng)前線程進(jìn)入了同步鎖(synchronized)剔氏,sleep方法并不會(huì)釋放鎖塑猖,即使當(dāng)前線程使用sleep方法讓出了cpu,但其他被同步鎖擋住了的線程也無法得到執(zhí)行谈跛。
2羊苟、wait是指在一個(gè)已經(jīng)進(jìn)入了同步鎖的線程內(nèi),讓自己暫時(shí)讓出同步鎖感憾,以便其他正在等待此鎖的線程可以得到同步鎖并運(yùn)行
2.2蜡励、只有其他線程調(diào)用了notify方法,調(diào)用wait方法的線程就會(huì)解除wait狀態(tài)和程序可以再次得到鎖后繼續(xù)向下運(yùn)行阻桅。
【注意】notify并不釋放鎖凉倚,只是告訴調(diào)用過wait方法的線程可以去參與獲得鎖的競(jìng)爭(zhēng)了,但不是馬上得到鎖嫂沉,因?yàn)殒i還在別人手里稽寒,別人還沒釋放。如果notify方法后面的代碼還有很多趟章,需要這些代碼執(zhí)行完后才會(huì)釋放鎖
【總結(jié)】notify只是告訴wait()的線程什么時(shí)候可以去繼續(xù)去申請(qǐng)鎖了
package com.example.demo.thread;
public class MultiThread {
private static class Thread1 implements Runnable {
@Override
public void run() {
//由于 Thread1和下面Thread2內(nèi)部run方法要用同一對(duì)象作為監(jiān)視器杏糙,
// 如果用this則Thread1和Threa2的this不是同一對(duì)象所以用MultiThread.class這個(gè)字節(jié)碼對(duì)象,
// 當(dāng)前虛擬機(jī)里引用這個(gè)變量時(shí)指向的都是同一個(gè)對(duì)象
synchronized (MultiThread.class) {
System.out.println("enter thread1 ...");
System.out.println("thread1 is waiting");
try {
//釋放鎖有兩種方式:
// (1)程序自然離開監(jiān)視器的范圍尤揣,即離開synchronized關(guān)鍵字管轄的代碼范圍
//(2)在synchronized關(guān)鍵字管轄的代碼內(nèi)部調(diào)用監(jiān)視器對(duì)象的wait()方法搔啊。
// 這里使用wait方法
MultiThread.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 is going on ...");
System.out.println("thread1 is being over!");
}
}
}
private static class Thread2 implements Runnable {
@Override
public void run() {
//notify方法并不釋放鎖,即使thread2調(diào)用了下面的sleep方法休息10ms北戏,但thread1仍然不會(huì)執(zhí)行
//因?yàn)閠hread2沒有釋放鎖负芋,所以Thread1得不到鎖而無法執(zhí)行
synchronized (MultiThread.class) {
System.out.println("enter thread2 ...");
System.out.println("thread2 notify other thread can release wait status ...");
MultiThread.class.notify();
System.out.println("thread2 is sleeping ten millisecond ...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2 is going on ...");
System.out.println("thread2 is being over!");
}
}
}
public static void main(String[] args) {
new Thread(new Thread1()).start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Thread2()).start();
}
}
【總結(jié):】首先先執(zhí)行了thread1,打印兩句話之后調(diào)用了wait方法,讓出了本鎖,然后往下執(zhí)行旧蛾,到線程thread2莽龟,它確實(shí)獲得到了本鎖,才會(huì)執(zhí)行下面的語句锨天,證明了wait方法確實(shí)是讓出了本鎖毯盈。然后打印了兩句話,調(diào)用了notify()方法病袄,告知thread1你可以去執(zhí)行了搂赋。但是此時(shí)thread1沒有鎖,鎖被thread2占用了益缠。thread2繼續(xù)執(zhí)行脑奠。打印一句話之后,調(diào)用sleep()方法讓出cpu幅慌,但是它不讓出鎖宋欺,所以thread1還是拿不到鎖,執(zhí)行不了胰伍,thread2睡眠完了之后齿诞,繼續(xù)往下打印兩句話,等執(zhí)行完成之后讓出改鎖骂租,thread1在繼續(xù)執(zhí)行祷杈。結(jié)果打印如上。
8.線程的 run()和 start()有什么區(qū)別渗饮?
1.start()方法來啟動(dòng)線程吠式,真正實(shí)現(xiàn)了多線程運(yùn)行。這時(shí)無需等待run方法體代碼執(zhí)行完畢抽米,可以直接繼續(xù)執(zhí)行下面的代碼特占;通過調(diào)用Thread類的start()方法來啟動(dòng)一個(gè)線程, 這時(shí)此線程是處于就緒狀態(tài)云茸, 并沒有運(yùn)行是目。 然后通過此Thread類調(diào)用方法run()來完成其運(yùn)行操作的, 這里方法run()稱為線程體标捺,它包含了要執(zhí)行的這個(gè)線程的內(nèi)容懊纳, run()方法運(yùn)行結(jié)束, 此線程終止亡容。然后CPU再調(diào)度其它線程嗤疯。
2.run()方法當(dāng)作普通方法的方式調(diào)用。程序還是要順序執(zhí)行闺兢,要等待run方法體執(zhí)行完畢后茂缚,才可繼續(xù)執(zhí)行下面的代碼; 程序中只有主線程這一個(gè)線程, 其程序執(zhí)行路徑還是只有一條脚囊, 這樣就沒有達(dá)到多線程的目的龟糕。
1、繼承 java.lang.Thread 類悔耘;
2讲岁、實(shí)現(xiàn) java.lang.Runnable接口;
其中 Thread 類也是實(shí)現(xiàn)了 Runnable 接口衬以,而 Runnable 接口定義了唯一的一個(gè) run() 方法缓艳,所以基于 Thread 和 Runnable 創(chuàng)建多線程都需要實(shí)現(xiàn) run() 方法,是多線程真正運(yùn)行的主方法看峻。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
而 start() 方法則是 Thread 類的方法郎任,用來異步啟動(dòng)一個(gè)線程,然后主線程立刻返回备籽。該啟動(dòng)的線程不會(huì)馬上運(yùn)行,會(huì)放到等待隊(duì)列中等待 CPU 調(diào)度分井,只有線程真正被 CPU 調(diào)度時(shí)才會(huì)調(diào)用 run() 方法執(zhí)行车猬。
那么你會(huì)問了,為什么要有兩個(gè)方法尺锚,直接用一個(gè) run() 方法不就行了嗎V槿颉? 還真不行瘫辩,如果直接調(diào)用 run() 方法伏嗜,那就等于調(diào)用了一個(gè)普通的同步方法,達(dá)不到多線程運(yùn)行的異步執(zhí)行伐厌,來看下面的例子承绸。
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Java技術(shù)棧");
});
long start = System.currentTimeMillis();
thread.start();
System.out.println(System.currentTimeMillis() - start);
start = System.currentTimeMillis();
thread.run();
System.out.println(System.currentTimeMillis() - start);
}
程序輸出:
0
Java技術(shù)棧
3000
Java技術(shù)棧
從程序輸出結(jié)果可以看出,啟動(dòng) start 方法前后只用了 0 毫秒挣轨,而啟動(dòng) run 方法則阻塞了 3000 毫秒等程序執(zhí)行完再繼續(xù)執(zhí)行军熏,這就是同步與異步的一個(gè)最重要的區(qū)別。
9.線程池都有哪些狀態(tài)卷扮?
1.RUNNING:這是最正常的狀態(tài)荡澎,接受新的任務(wù),處理等待隊(duì)列中的任務(wù)晤锹。線程池的初始化狀態(tài)是RUNNING摩幔。線程池被一旦被創(chuàng)建,就處于RUNNING狀態(tài)鞭铆,并且線程池中的任務(wù)數(shù)為0或衡。
2.SHUTDOWN:不接受新的任務(wù)提交,但是會(huì)繼續(xù)處理等待隊(duì)列中的任務(wù)。調(diào)用線程池的shutdown()方法時(shí)薇宠,線程池由RUNNING -> SHUTDOWN偷办。
3.STOP:不接受新的任務(wù)提交,不再處理等待隊(duì)列中的任務(wù)澄港,中斷正在執(zhí)行任務(wù)的線程椒涯。調(diào)用線程池的shutdownNow()方法時(shí),線程池由(RUNNING or SHUTDOWN ) -> STOP回梧。
4.TIDYING:所有的任務(wù)都銷毀了废岂,workCount 為 0,線程池的狀態(tài)在轉(zhuǎn)換為 TIDYING 狀態(tài)時(shí)狱意,會(huì)執(zhí)行鉤子方法 terminated()湖苞。因?yàn)閠erminated()在ThreadPoolExecutor類中是空的,所以用戶想在線程池變?yōu)門IDYING時(shí)進(jìn)行相應(yīng)的處理详囤;可以通過重載terminated()函數(shù)來實(shí)現(xiàn)财骨。
當(dāng)線程池在SHUTDOWN狀態(tài)下,阻塞隊(duì)列為空并且線程池中執(zhí)行的任務(wù)也為空時(shí)藏姐,就會(huì)由 SHUTDOWN -> TIDYING隆箩。
當(dāng)線程池在STOP狀態(tài)下,線程池中執(zhí)行的任務(wù)為空時(shí)羔杨,就會(huì)由STOP -> TIDYING捌臊。
5.TERMINATED:線程池處在TIDYING狀態(tài)時(shí),執(zhí)行完terminated()之后兜材,就會(huì)由 TIDYING -> TERMINATED理澎。
10.多線程鎖的升級(jí)原理是什么?
鎖的級(jí)別從低到高:
無鎖 -> 偏向鎖 -> 輕量級(jí)鎖 -> 重量級(jí)鎖
鎖分級(jí)別原因:
沒有優(yōu)化以前曙寡,sychronized是重量級(jí)鎖(悲觀鎖)糠爬,使用 wait 和 notify、notifyAll 來切換線程狀態(tài)非常消耗系統(tǒng)資源举庶;線程的掛起和喚醒間隔很短暫秩铆,這樣很浪費(fèi)資源,影響性能灯变。所以 JVM 對(duì) sychronized 關(guān)鍵字進(jìn)行了優(yōu)化殴玛,把鎖分為 無鎖、偏向鎖添祸、輕量級(jí)鎖滚粟、重量級(jí)鎖 狀態(tài)。
無鎖:沒有對(duì)資源進(jìn)行鎖定刃泌,所有的線程都能訪問并修改同一個(gè)資源凡壤,但同時(shí)只有一個(gè)線程能修改成功署尤,其他修改失敗的線程會(huì)不斷重試直到修改成功。
偏向鎖:對(duì)象的代碼一直被同一線程執(zhí)行亚侠,不存在多個(gè)線程競(jìng)爭(zhēng)曹体,該線程在后續(xù)的執(zhí)行中自動(dòng)獲取鎖,降低獲取鎖帶來的性能開銷硝烂。偏向鎖箕别,指的就是偏向第一個(gè)加鎖線程,該線程是不會(huì)主動(dòng)釋放偏向鎖的滞谢,只有當(dāng)其他線程嘗試競(jìng)爭(zhēng)偏向鎖才會(huì)被釋放串稀。
偏向鎖的撤銷,需要在某個(gè)時(shí)間點(diǎn)上沒有字節(jié)碼正在執(zhí)行時(shí)狮杨,先暫停擁有偏向鎖的線程母截,然后判斷鎖對(duì)象是否處于被鎖定狀態(tài)。如果線程不處于活動(dòng)狀態(tài)橄教,則將對(duì)象頭設(shè)置成無鎖狀態(tài)清寇,并撤銷偏向鎖;
如果線程處于活動(dòng)狀態(tài)护蝶,升級(jí)為輕量級(jí)鎖的狀態(tài)华烟。
輕量級(jí)鎖:輕量級(jí)鎖是指當(dāng)鎖是偏向鎖的時(shí)候,被第二個(gè)線程 B 所訪問滓走,此時(shí)偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,線程 B 會(huì)通過自旋的形式嘗試獲取鎖帽馋,線程不會(huì)阻塞搅方,從而提高性能。
當(dāng)前只有一個(gè)等待線程绽族,則該線程將通過自旋進(jìn)行等待姨涡。但是當(dāng)自旋超過一定的次數(shù)時(shí),輕量級(jí)鎖便會(huì)升級(jí)為重量級(jí)鎖吧慢;當(dāng)一個(gè)線程已持有鎖涛漂,另一個(gè)線程在自旋,而此時(shí)又有第三個(gè)線程來訪時(shí)检诗,輕量級(jí)鎖也會(huì)升級(jí)為重量級(jí)鎖匈仗。
重量級(jí)鎖:指當(dāng)有一個(gè)線程獲取鎖之后,其余所有等待獲取該鎖的線程都會(huì)處于阻塞狀態(tài)逢慌。
重量級(jí)鎖通過對(duì)象內(nèi)部的監(jiān)視器(monitor)實(shí)現(xiàn)悠轩,而其中 monitor 的本質(zhì)是依賴于底層操作系統(tǒng)的 Mutex Lock 實(shí)現(xiàn),操作系統(tǒng)實(shí)現(xiàn)線程之間的切換需要從用戶態(tài)切換到內(nèi)核態(tài)攻泼,切換成本非常高火架。
11.什么是死鎖鉴象?
線程死鎖是指由于兩個(gè)或者多個(gè)線程互相持有對(duì)方所需要的資源,導(dǎo)致這些線程處于等待狀態(tài)何鸡,無法繼續(xù)執(zhí)行纺弊。當(dāng)線程進(jìn)入對(duì)象的synchronized代碼塊時(shí),便占有了資源骡男,直到它退出該代碼塊或者調(diào)用wait方法淆游,才釋放資源,在此期間洞翩,其他線程將不能進(jìn)入該代碼塊稽犁。當(dāng)線程互相持有對(duì)方所需要的資源時(shí),會(huì)互相等待對(duì)方釋放資源骚亿,如果線程都不主動(dòng)釋放所占有的資源已亥,將產(chǎn)生死鎖。
當(dāng)然死鎖的產(chǎn)生是必須要滿足一些特定條件的:
1.互斥條件:進(jìn)程對(duì)于所分配到的資源具有排它性来屠,即一個(gè)資源只能被一個(gè)進(jìn)程占用虑椎,直到被該進(jìn)程釋放
2.請(qǐng)求和保持條件:一個(gè)進(jìn)程因請(qǐng)求被占用資源而發(fā)生阻塞時(shí),對(duì)已獲得的資源保持不放俱笛。
3.不剝奪條件:任何一個(gè)資源在沒被該進(jìn)程釋放之前捆姜,任何其他進(jìn)程都無法對(duì)他剝奪占用
4.循環(huán)等待條件:當(dāng)發(fā)生死鎖時(shí),所等待的進(jìn)程必定會(huì)形成一個(gè)環(huán)路(類似于死循環(huán))迎膜,造成永久阻塞泥技。