死鎖 - 必要條件
互斥條件:進(jìn)程要求對(duì)所分配的資源(如打印機(jī))進(jìn)行排他性控制,即在一段時(shí)間內(nèi)某資源僅為一個(gè)進(jìn)程所占有。此時(shí)若有其他進(jìn)程請(qǐng)求該資源,則請(qǐng)求進(jìn)程只能等待。
請(qǐng)求和保持條件:進(jìn)程已經(jīng)保持了至少一個(gè)資源,但又提出了新的資源請(qǐng)求,而該資源已被其他進(jìn)程占有,此時(shí)請(qǐng)求進(jìn)程被阻塞,但對(duì)自己已獲得的資源保持不放。
不剝奪條件:進(jìn)程所獲得的資源在未使用完畢之前,不能被其他進(jìn)程強(qiáng)行奪走,即只能由獲得該資源的進(jìn)程自己來(lái)釋放(只能是主動(dòng)釋放)。
環(huán)路等待條件:存在一種進(jìn)程資源的循環(huán)等待鏈,鏈中每一個(gè)進(jìn)程已獲得的資源同時(shí)被鏈中下一個(gè)進(jìn)程所請(qǐng)求。即存在一個(gè)處于等待狀態(tài)的進(jìn)程集合{Pl, P2, ..., pn},其中Pi等 待的資源被P(i+1)占有(i=0, 1, ..., n-1),Pn等待的資源被P0占有,如圖1所示。
代碼演示:
/**
* 一個(gè)簡(jiǎn)單的死鎖類(lèi)
* 當(dāng)DeadLock類(lèi)的對(duì)象flag==1時(shí)(td1),先鎖定o1,睡眠500毫秒
* 而td1在睡眠的時(shí)候另一個(gè)flag==0的對(duì)象(td2)線(xiàn)程啟動(dòng),先鎖定o2,睡眠500毫秒
* td1睡眠結(jié)束后需要鎖定o2才能繼續(xù)執(zhí)行,而此時(shí)o2已被td2鎖定;
* td2睡眠結(jié)束后需要鎖定o1才能繼續(xù)執(zhí)行,而此時(shí)o1已被td1鎖定;
* td1、td2相互等待,都需要得到對(duì)方鎖定的資源才能繼續(xù)執(zhí)行,從而死鎖。
*/
@Slf4j
public class DeadLock implements Runnable {
public int flag = 1;
//靜態(tài)對(duì)象是類(lèi)的所有對(duì)象共享的
private static Object o1 = new Object(), o2 = new Object();
@Override
public void run() {
log.info("flag:{}", flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
log.info("1");
}
}
}
if (flag == 0) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
log.info("0");
}
}
}
}
public static void main(String[] args) {
DeadLock td1 = new DeadLock();
DeadLock td2 = new DeadLock();
td1.flag = 1;
td2.flag = 0;
//td1,td2都處于可執(zhí)行狀態(tài),但JVM線(xiàn)程調(diào)度先執(zhí)行哪個(gè)線(xiàn)程是不確定的。
//td2的run()可能在td1的run()之前運(yùn)行
new Thread(td1).start();
new Thread(td2).start();
}
}
多線(xiàn)程并發(fā)最佳實(shí)踐
使用本地變量
使用不可變類(lèi)
最小化鎖的作用于范圍:S=1/(1-a+a/n)
使用線(xiàn)程池的Executor,而不是直接new Thread執(zhí)行
寧可使用同步也不要使用線(xiàn)程的wait和notify
使用BlockingQueue實(shí)現(xiàn)生產(chǎn)-消費(fèi)模式
使用并發(fā)集合而不是加了鎖的同步集合
使用Semaphore創(chuàng)建有界的訪(fǎng)問(wèn)
寧可使用同步代碼塊,也不要使用同步的方法
避免使用靜態(tài)變量