互联网大厂高频重点面试题
1.volatile是Java虛擬機(jī)提供的輕量級(jí)的同步機(jī)制
volatile提供的輕量級(jí)的同步機(jī)制
1.1保證可見(jiàn)性
1.2不保證原子性
1.3禁止指令重排
1.4JMM(Java Memory Model)
Java內(nèi)存模型,簡(jiǎn)稱(chēng)JMM ,其本身是一種抽象的概念并不是真實(shí)存在,它描述的是一組規(guī)則或規(guī)范,通過(guò)這組規(guī)范定義了程序中的各個(gè)變量(包括實(shí)例字段,靜態(tài)字段和構(gòu)成數(shù)組的元素)的訪問(wèn)方式。
JMM關(guān)于同步的規(guī)定:
1.線程加鎖前,必須把共享的值刷新回主內(nèi)存
2.線程加鎖前,必須讀取內(nèi)存的最新值到自己的工作空間
3.加瑣解鎖是同一把鎖
?
由于JVM運(yùn)行程序的實(shí)體是線程,而每個(gè)線程創(chuàng)建時(shí)jvm都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱(chēng)為??臻g)工作內(nèi)存是每個(gè)線程的私有數(shù)據(jù)區(qū)域,而java內(nèi)存模型中規(guī)定所有的變量都存儲(chǔ)在主內(nèi)存,主內(nèi)存是共享內(nèi)存區(qū)域,所有的線程都可以訪問(wèn),但線程對(duì)變量的操作(讀取賦值等)必須在工作內(nèi)存中進(jìn)行,首先要將變量拷貝到自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完成后將變量寫(xiě)回主內(nèi)存,不能直接操作內(nèi)存中的變量,各個(gè)線程的工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本拷貝,因此不同的線程無(wú)法訪問(wèn)對(duì)方的工作內(nèi)存,線程間的通信(傳值)必須通過(guò)主內(nèi)存來(lái)完成。
?
1.4.1可見(jiàn)性
通過(guò)前面對(duì)JMM的介紹,我們知道各個(gè)線程對(duì)主內(nèi)存中共享變量的操作都是各個(gè)線程各自拷貝到自己的工作內(nèi)存操作后再寫(xiě)回主內(nèi)存中的.這就可能存在一個(gè)線程AAA修改了共享變量X的值還未寫(xiě)回主內(nèi)存中時(shí) ,另外一個(gè)線程BBB又對(duì)內(nèi)存中的一個(gè)共享變量X進(jìn)行操作,但此時(shí)A線程工作內(nèi)存中的共享比那里X對(duì)線程B來(lái)說(shuō)并不不可見(jiàn).這種工作內(nèi)存與主內(nèi)存同步延遲現(xiàn)象就造成了可見(jiàn)性問(wèn)題.
1.4.2原子性
number++在多線程下是非線程安全的,如何不加synchronized解決?
?
VolatileDemo代碼演示可見(jiàn)性+原子性代碼
?
有序性
計(jì)算機(jī)在執(zhí)行程序時(shí),為了提高性能,編譯器和處理器常常會(huì)做指令重排,一把分為以下3中單線程環(huán)境里面確保程序最終執(zhí)行結(jié)果和代碼順序執(zhí)行的結(jié)果一致.處理器在進(jìn)行重新排序是必須要考慮指令之間的數(shù)據(jù)依賴(lài)性多線程環(huán)境中線程交替執(zhí)行,由于編譯器優(yōu)化重排的存在,兩個(gè)線程使用的變量能否保持一致性是無(wú)法確定的,結(jié)果無(wú)法預(yù)測(cè)
內(nèi)存屏障(Memory Barrier)
利用該特征實(shí)現(xiàn)volatile的內(nèi)存可見(jiàn)性 由于編譯器和處理器都能執(zhí)行指令重排優(yōu)化。如果在指令間插入一條Memory Barrier 則會(huì)告訴編譯器和CPU,不管什么指令都不能和這條Memory Barrier指令重排序,也就是說(shuō)通過(guò)插入內(nèi)存屏障禁止在內(nèi)存屏障前后的指令執(zhí)行重排序優(yōu)化。內(nèi)存屏障另外一個(gè)作用是強(qiáng)制刷出各種CPU的緩存數(shù)據(jù),因此任何CPU上的線程都能讀取到這些數(shù)據(jù)上的最新版本
重排1
public void mySort(){ int x=11;//語(yǔ)句1 int y=12;//語(yǔ)句2 x=x+5;//語(yǔ)句3 y=x*x;//語(yǔ)句4 } 123421341324 問(wèn)題:請(qǐng)問(wèn)語(yǔ)句4 可以重排后變成第一條碼?存在數(shù)據(jù)的依賴(lài)性 沒(méi)辦法排到第一個(gè)重排2
int a ,b ,x,y=0;線程1線程2x=a;y=b;b=1;a=2;x=0 y=0 如果編譯器對(duì)這段代碼進(jìn)行執(zhí)行重排優(yōu)化后,可能出現(xiàn)下列情況:線程1線程2b=1;a=2;x=a;y=b;x=2 y=1 這也就說(shuō)明在多線程環(huán)境下,由于編譯器優(yōu)化重排的存在,兩個(gè)線程使用的變量能否保持一致是無(wú)法確定的.禁止指令重排小總結(jié)(了解)
工作內(nèi)存與內(nèi)存同步延遲現(xiàn)象導(dǎo)致的可見(jiàn)性問(wèn)題 可以使用synchronized或volatile關(guān)鍵字解決,他們都可以使一個(gè)線程修改后的變量立即對(duì)其他的線程可見(jiàn)對(duì)指令重排序?qū)е碌目梢?jiàn)性問(wèn)題和有序性問(wèn)題 可以利用volatile關(guān)鍵字解決,因?yàn)関olatile的另一個(gè)作用就是禁止重排序優(yōu)化?DCL(雙端檢測(cè)機(jī)制)
DCL(雙端檢測(cè)機(jī)制)不一定線程安全,原因是有指令重排序的存在,加入volatile可以禁止指令重排,原因在某一線程執(zhí)行到第一次檢測(cè),讀取到的instance不為null時(shí),instance的引用對(duì)象可能沒(méi)有完成初始化。 instance = new SingletonDemo();可分為以下3步完成(偽代碼)//1.分配對(duì)象內(nèi)存空間
memory = allocate();
//2.初始化對(duì)象
instance(memory);
//3.設(shè)置instance指向剛分配的內(nèi)存地址,此時(shí) instance != null
instance = memory;
步驟2和3不存在數(shù)據(jù)依賴(lài)關(guān)系,而且無(wú)論重排前還是重排后的程序的執(zhí)行結(jié)果在單線程中并沒(méi)有改變,因此這種重排優(yōu)化是允許的
//1.分配對(duì)象內(nèi)存空間
memory = allocate(); //3.設(shè)置instance指向剛分配的內(nèi)存地址,此時(shí) instance != null,但是對(duì)象還沒(méi)有初始化完成!
instance = memory; //2.初始化對(duì)象
instance(memory);
但是指令重排只會(huì)保證串行語(yǔ)義的執(zhí)行一致性(單線程),但并不會(huì)關(guān)心多線程間的語(yǔ)義一致性。
所以當(dāng)一條線程訪問(wèn)instance不為null時(shí),由于instance實(shí)例并未初始化完成,也就造成線程安全問(wèn)題。
你在哪些地方用到過(guò)volatile?
3.1 單例模式DCL代碼
public class SingletonDemo { private static volatile SingletonDemo instance=null; private SingletonDemo(){ System.out.println(Thread.currentThread().getName()+"\t 構(gòu)造方法"); } /** * 雙重檢測(cè)機(jī)制 * @return */ public static SingletonDemo getInstance(){ if(instance==null){ synchronized (SingletonDemo.class){ if(instance==null){ instance=new SingletonDemo(); } } } return instance; } public static void main(String[] args) { for (int i = 1; i <=10; i++) { new Thread(() ->{ SingletonDemo.getInstance(); },String.valueOf(i)).start(); } }} View Code?
3.2代理模式volatile分析
DCL(雙端檢鎖) 機(jī)制不一定線程安全,原因是有指令重排的存在,加入volatile可以禁止指令重排 原因在于某一個(gè)線程在執(zhí)行到第一次檢測(cè),讀取到的instance不為null時(shí),instance的引用對(duì)象可能沒(méi)有完成初始化.instance=new SingletonDem(); 可以分為以下步驟(偽代碼) memory=allocate();//1.分配對(duì)象內(nèi)存空間instance(memory);//2.初始化對(duì)象instance=memory;//3.設(shè)置instance的指向剛分配的內(nèi)存地址,此時(shí)instance!=null 步驟2和步驟3不存在數(shù)據(jù)依賴(lài)關(guān)系.而且無(wú)論重排前還是重排后程序執(zhí)行的結(jié)果在單線程中并沒(méi)有改變,因此這種重排優(yōu)化是允許的.memory=allocate();//1.分配對(duì)象內(nèi)存空間instance=memory;//3.設(shè)置instance的指向剛分配的內(nèi)存地址,此時(shí)instance!=null 但對(duì)象還沒(méi)有初始化完.instance(memory);//2.初始化對(duì)象但是指令重排只會(huì)保證串行語(yǔ)義的執(zhí)行一致性(單線程) 并不會(huì)關(guān)心多線程間的語(yǔ)義一致性所以當(dāng)一條線程訪問(wèn)instance不為null時(shí),由于instance實(shí)例未必完成初始化,也就造成了線程安全問(wèn)題. View Code?
CAS
1.比較并交換
/** * Description * * @author veliger@163.com * @version 1.0 * @date 2019-04-12 9:57 * 1.什么是CAS ? ===> compareAndSet * 比較并交換 **/public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.compareAndSet(5, 2019)+"\t current"+atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5, 2014)+"\t current"+atomicInteger.get());
}}
?
2.CAS底層原理?如果知道,談?wù)勀銓?duì)UnSafe的理解
atomicInteger.getAndIncrement();
atomicInteger.getAndIncrement()方法的源代碼: /** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } 印出來(lái)一個(gè)問(wèn)題:UnSafe類(lèi)是什么?UnSafe
1.UnSafe 是CAS的核心類(lèi) 由于Java 方法無(wú)法直接訪問(wèn)底層 ,需要通過(guò)本地(native)方法來(lái)訪問(wèn),UnSafe相當(dāng)于一個(gè)后面,基于該類(lèi)可以直接操作特額定的內(nèi)存數(shù)據(jù).UnSafe類(lèi)在于sun.misc包中,其內(nèi)部方法操作可以向C的指針一樣直接操作內(nèi)存,因?yàn)镴ava中CAS操作的助興依賴(lài)于UNSafe類(lèi)的方法.注意UnSafe類(lèi)中所有的方法都是native修飾的,也就是說(shuō)UnSafe類(lèi)中的方法都是直接調(diào)用操作底層資源執(zhí)行響應(yīng)的任務(wù)
2.變量ValueOffset,便是該變量在內(nèi)存中的偏移地址,因?yàn)閁nSafe就是根據(jù)內(nèi)存偏移地址獲取數(shù)據(jù)的
3.變量value和volatile修飾,保證了多線程之間的可見(jiàn)性.
?
3.CAS是什么
unSafe.getAndIncrement
var1 AtomicInteger對(duì)象本身.var2 該對(duì)象值的引用地址var4 需要變動(dòng)的數(shù)值var5 是用過(guò)var1 var2找出內(nèi)存中紳士的值用該對(duì)象當(dāng)前的值與var5比較如果相同,更新var5的值并且返回true如果不同,繼續(xù)取值然后比較,直到更新完成假設(shè)線程A和線程B兩個(gè)線程同時(shí)執(zhí)行g(shù)etAndAddInt操作(分別在不同的CPU上):
1.AtomicInteger里面的value原始值為3,即主內(nèi)存中AtomicInteger的value為3,根據(jù)JMM模型,線程A和線程B各自持有一份值為3的value的副本分別到各自的工作內(nèi)存.
2.線程A通過(guò)getIntVolatile(var1,var2) 拿到value值3,這是線程A被掛起.
3.線程B也通過(guò)getIntVolatile(var1,var2) 拿到value值3,此時(shí)剛好線程B沒(méi)有被掛起并執(zhí)行compareAndSwapInt方法比較內(nèi)存中的值也是3 成功修改內(nèi)存的值為4 線程B打完收工 一切OK.
4.這是線程A恢復(fù),執(zhí)行compareAndSwapInt方法比較,發(fā)現(xiàn)自己手里的數(shù)值和內(nèi)存中的數(shù)字4不一致,說(shuō)明該值已經(jīng)被其他線程搶先一步修改了,那A線程修改失敗,只能重新來(lái)一遍了.
5.線程A重新獲取value值,因?yàn)樽兞縱alue是volatile修飾,所以其他線程對(duì)他的修改,線程A總是能夠看到,線程A繼續(xù)執(zhí)行compareAndSwapInt方法進(jìn)行比較替換,直到成功.
?
底層匯編
?
簡(jiǎn)單版小總結(jié)
?
4.CAS缺點(diǎn)
循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)很大
只能保證一個(gè)共享變量的原子性
引出來(lái)ABA問(wèn)題???
?
原子類(lèi)AtomicInteger的ABA問(wèn)題談?wù)?原子更新引用知道嗎
ABA問(wèn)題的產(chǎn)生
?
原子引用
AtomicReferenceDemo
/** * Description: * * @author veliger@163.com * @date 2019-04-12 21:23 **/@Getter
@Setter
@AllArgsConstructor
@ToStringclass User{
private String name;
private int age;
}
public class AtomicReferenceDemo {
public static void main(String[] args) {
User zs = new User("zs", 22);
User ls = new User("ls", 22);
AtomicReference<User> userAtomicReference = new AtomicReference<>();
userAtomicReference.set(zs);
System.out.println(userAtomicReference.compareAndSet(zs, ls)+"\t"+userAtomicReference.get().toString());
System.out.println(userAtomicReference.compareAndSet(zs, ls)+"\t"+userAtomicReference.get().toString());
}
}
?
時(shí)間戳原子引用
AtomicStampedReference /** * Description: ABA問(wèn)題的解決 * * @author veliger @163.com * @date 2019-04-12 21:30 **/ public class ABADemo { private static AtomicReference<Integer> atomicReference=new AtomicReference<>(100); private static AtomicStampedReference<Integer> stampedReference=new AtomicStampedReference<>(100,1); public static void main(String[] args) { System.out.println("===以下是ABA問(wèn)題的產(chǎn)生==="); new Thread(()->{ atomicReference.compareAndSet(100,101); atomicReference.compareAndSet(101,100); },"t1").start(); new Thread(()->{ //先暫停1秒 保證完成ABA try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get()); },"t2").start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("===以下是ABA問(wèn)題的解決==="); new Thread(()->{ int stamp = stampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"\t 第1次版本號(hào)"+stamp+"\t值是"+stampedReference.getReference()); //暫停1秒鐘t3線程 try { TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) { e.printStackTrace();} stampedReference.compareAndSet(100,101,stampedReference.getStamp(),stampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName()+"\t 第2次版本號(hào)"+stampedReference.getStamp()+"\t值是"+stampedReference.getReference()); stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName()+"\t 第3次版本號(hào)"+stampedReference.getStamp()+"\t值是"+stampedReference.getReference()); },"t3").start(); new Thread(()->{ int stamp = stampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"\t 第1次版本號(hào)"+stamp+"\t值是"+stampedReference.getReference()); //保證線程3完成1次ABA try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } boolean result = stampedReference.compareAndSet(100, 2019, stamp, stamp + 1); System.out.println(Thread.currentThread().getName()+"\t 修改成功否"+result+"\t最新版本號(hào)"+stampedReference.getStamp()); System.out.println("最新的值\t"+stampedReference.getReference()); },"t4").start(); } ABADemo我們知道ArrayList是線程不安全,請(qǐng)編寫(xiě)一個(gè)不安全的案例并給出解決方案
公平鎖和非公平鎖
公平鎖 是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖類(lèi)似排隊(duì)打飯 先來(lái)后到非公平鎖 是指在多線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取到鎖,在高并發(fā)的情況下,有可能造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象?
公平鎖/非公平鎖 并發(fā)包ReentrantLock的創(chuàng)建可以指定構(gòu)造函數(shù)的boolean類(lèi)型來(lái)得到公平鎖或者非公平鎖 默認(rèn)是非公平鎖 Java ReentrantLock而言,通過(guò)構(gòu)造哈數(shù)指定該鎖是否是公平鎖 默認(rèn)是非公平鎖 非公平鎖的優(yōu)點(diǎn)在于吞吐量必公平鎖大. 對(duì)于synchronized而言 也是一種非公平鎖.可重入鎖(又名遞歸鎖)
ReentrantLock/synchronized就是一個(gè)典型的可重入鎖 可重入鎖最大的作用就是避免死鎖?
ReenterLockDemo
package cn.atguigu.interview.study.thread; class Phone{ public synchronized void sendSms() throws Exception{ System.out.println(Thread.currentThread().getName()+"\tsendSms"); sendEmail(); } public synchronized void sendEmail() throws Exception{ System.out.println(Thread.currentThread().getName()+"\tsendEmail"); }}/** * Description: * 可重入鎖(也叫做遞歸鎖) * 指的是同一先生外層函數(shù)獲得鎖后,內(nèi)層敵對(duì)函數(shù)任然能獲取該鎖的代碼 * 在同一線程外外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖 * * 也就是說(shuō),線程可以進(jìn)入任何一個(gè)它已經(jīng)標(biāo)記的鎖所同步的代碼塊 * * @author veliger@163.com * @date 2019-04-12 23:36 **/ public class ReenterLockDemo { /** * t1 sendSms * t1 sendEmail * t2 sendSms * t2 sendEmail * @param args */ public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ try { phone.sendSms(); } catch (Exception e) { e.printStackTrace(); } },"t1").start(); new Thread(()->{ try { phone.sendSms(); } catch (Exception e) { e.printStackTrace(); } },"t2").start(); }} 參考1 package cn.atguigu.interview.study.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; class Phone implements Runnable { private Lock lock = new ReentrantLock(); @Override public void run() { get(); } private void get() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "\tget"); set(); } finally { lock.unlock(); } } private void set() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "\tset"); } finally { lock.unlock(); } }}/** * Description: * 可重入鎖(也叫做遞歸鎖) * 指的是同一先生外層函數(shù)獲得鎖后,內(nèi)層敵對(duì)函數(shù)任然能獲取該鎖的代碼 * 在同一線程外外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖 * <p> * 也就是說(shuō),線程可以進(jìn)入任何一個(gè)它已經(jīng)標(biāo)記的鎖所同步的代碼塊 * * @author veliger@163.com * @date 2019-04-12 23:36 **/ public class ReenterLockDemo { /** * Thread-0 get * Thread-0 set * Thread-1 get * Thread-1 set * * @param args */ public static void main(String[] args) { Phone phone = new Phone(); Thread t3 = new Thread(phone); Thread t4 = new Thread(phone); t3.start(); t4.start(); }} 參考2自旋鎖
SpinLockDemo
獨(dú)占鎖(寫(xiě))/共享鎖(讀)/互斥鎖
/** * 資源類(lèi) */class MyCaChe { /** * 保證可見(jiàn)性 */ private volatile Map<String, Object> map = new HashMap<>(); private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); /** * 寫(xiě) * * @param key * @param value */ public void put(String key, Object value) { reentrantReadWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + "\t正在寫(xiě)入" + key); //模擬網(wǎng)絡(luò)延時(shí) try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } map.put(key, value); System.out.println(Thread.currentThread().getName() + "\t正在完成"); } finally { reentrantReadWriteLock.writeLock().unlock(); } } /** * 讀 * * @param key */ public void get(String key) { reentrantReadWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + "\t正在讀取"); //模擬網(wǎng)絡(luò)延時(shí) try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } Object result = map.get(key); System.out.println(Thread.currentThread().getName() + "\t正在完成" + result); } finally { reentrantReadWriteLock.readLock().unlock(); } } public void clearCaChe() { map.clear(); }}/** * Description: * 多個(gè)線程同時(shí)操作 一個(gè)資源類(lèi)沒(méi)有任何問(wèn)題 所以為了滿足并發(fā)量 * 讀取共享資源應(yīng)該可以同時(shí)進(jìn)行 * 但是 * 如果有一個(gè)線程想去寫(xiě)共享資源來(lái) 就不應(yīng)該有其他線程可以對(duì)資源進(jìn)行讀或?qū)?* <p> * 小總結(jié): * 讀 讀能共存 * 讀 寫(xiě)不能共存 * 寫(xiě) 寫(xiě)不能共存 * 寫(xiě)操作 原子+獨(dú)占 整個(gè)過(guò)程必須是一個(gè)完成的統(tǒng)一整體 中間不允許被分割 被打斷 * * @author veliger@163.com * @date 2019-04-13 0:45 **/ public class ReadWriteLockDemo { public static void main(String[] args) { MyCaChe myCaChe = new MyCaChe(); for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(() -> { myCaChe.put(temp + "", temp); }, String.valueOf(i)).start(); } for (int i = 1; i <= 5; i++) { int finalI = i; new Thread(() -> { myCaChe.get(finalI + ""); }, String.valueOf(i)).start(); } }} ReadWriteLockDemo?
讀寫(xiě)鎖
?
?
CountDownLatch/CyclicBarrier/Semaphore
CountDownLatch
讓一些線程阻塞直到另外一些完成后才被喚醒
CountDownLatch主要有兩個(gè)方法,當(dāng)一個(gè)或多個(gè)線程調(diào)用await方法時(shí),調(diào)用線程會(huì)被阻塞.其他線程調(diào)用countDown方法計(jì)數(shù)器減1(調(diào)用countDown方法時(shí)線程不會(huì)阻塞),當(dāng)計(jì)數(shù)器的值變?yōu)?,因調(diào)用await方法被阻塞的線程會(huì)被喚醒,繼續(xù)執(zhí)行CountDownLatchDemo
public class CountDownLatchDemo { public static void main(String[] args) throws Exception { closeDoor(); } /** * 關(guān)門(mén)案例 * @throws InterruptedException */ private static void closeDoor() throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "\t" + "上完自習(xí)"); countDownLatch.countDown(); }, String.valueOf(i)).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "\t班長(zhǎng)鎖門(mén)離開(kāi)教室"); } } 關(guān)門(mén)案例 /** * Description * 枚舉的使用 * * @author veliger@163.com * @version 1.0 * @date 2019-04-13 10:14 **/ public enum CountryEnum { /** * */ ONE(1, "齊"), /** * */ TWO(2, "楚"), /** * */ THREE(3, "燕"), /** * */ FOUR(4, "趙"), /** * */ FIVE(5, "魏"), /** * */ SIX(6, "韓"); CountryEnum(Integer code, String name) { this.code = code; this.name = name; } @Getter private Integer code; @Getter private String name; public static CountryEnum forEach(int index) { CountryEnum[] countryEnums = CountryEnum.values(); for (CountryEnum countryEnum : countryEnums) { if (index == countryEnum.getCode()) { return countryEnum; } } return null; }} 枚舉的使用 public class CountDownLatchDemo { public static void main(String[] args) throws Exception { sixCountry(); } /** * 秦滅六國(guó) 一統(tǒng)華夏 * @throws InterruptedException */ private static void sixCountry() throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "\t" + "國(guó),滅亡"); countDownLatch.countDown(); }, CountryEnum.forEach(i).getName()).start(); } countDownLatch.await(); System.out.println("秦統(tǒng)一"); } } 秦滅六國(guó)CyclicBarrier
CyclicBarrier的字面意思是可循環(huán)(Cyclic) 使用的屏障(barrier).它要做的事情是,讓一組線程到達(dá)一個(gè)屏障(也可以叫做同步點(diǎn))時(shí)被阻塞,知道最后一個(gè)線程到達(dá)屏障時(shí),屏障才會(huì)開(kāi)門(mén),所有被屏障攔截的線程才會(huì)繼續(xù)干活,線程進(jìn)入屏障通過(guò)CyclicBarrier的await()方法.?
CyclicBarrierDemo
集齊7顆龍珠就能召喚神龍
public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{ System.out.println("召喚神龍"); }); for (int i = 1; i <=7; i++) { final int temp = i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t 收集到第"+ temp +"顆龍珠"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); } }} code?
Semaphore
信號(hào)量的主要用戶(hù)兩個(gè)目的,一個(gè)是用于多喝共享資源的相互排斥使用,另一個(gè)用于并發(fā)資源數(shù)的控制. /** * Description * * @author veliger@163.com * @version 1.0 * @date 2019-04-13 11:08 **/ public class SemaphoreDemo { public static void main(String[] args) { //模擬3個(gè)停車(chē)位 Semaphore semaphore = new Semaphore(3); //模擬6部汽車(chē) for (int i = 1; i <= 6; i++) { new Thread(() -> { try { //搶到資源 semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "\t搶到車(chē)位"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "\t 停3秒離開(kāi)車(chē)位"); } catch (InterruptedException e) { e.printStackTrace(); } finally { //釋放資源 semaphore.release(); } }, String.valueOf(i)).start(); } }} 搶車(chē)位?
阻塞隊(duì)列
隊(duì)列+阻塞隊(duì)列
阻塞隊(duì)列,顧名思義,首先它是一個(gè)隊(duì)列,而一個(gè)阻塞隊(duì)列在數(shù)據(jù)結(jié)構(gòu)中所起的作用大致如圖所示:\線程1往阻塞隊(duì)列中添加元素二線程2從隊(duì)列中移除元素當(dāng)阻塞隊(duì)列是空時(shí),從隊(duì)列中獲取元素的操作將會(huì)被阻塞.當(dāng)阻塞隊(duì)列是滿時(shí),往隊(duì)列中添加元素的操作將會(huì)被阻塞.同樣試圖往已滿的阻塞隊(duì)列中添加新圓度的線程同樣也會(huì)被阻塞,
知道其他線程從隊(duì)列中移除一個(gè)或者多個(gè)元素或者全清空隊(duì)列后使隊(duì)列重新變得空閑起來(lái)并后續(xù)新增.
?
為什么用?有什么好處?
在多線程領(lǐng)域:所謂阻塞,在某些情況下會(huì)掛起線程(即線程阻塞),一旦條件滿足,被掛起的線程優(yōu)惠被自動(dòng)喚醒為什么需要使用BlockingQueue好處是我們不需要關(guān)心什么時(shí)候需要阻塞線程,什么時(shí)候需要喚醒線程,因?yàn)锽lockingQueue都一手給你包辦好了在concurrent包 發(fā)布以前,
在多線程環(huán)境下,我們每個(gè)程序員都必須自己去控制這些細(xì)節(jié),尤其還要兼顧效率和線程安全,而這會(huì)給我們的程序帶來(lái)不小的復(fù)雜度.
?
BlockingQueue的核心方法
拋出異常當(dāng)阻塞隊(duì)列滿時(shí),再往隊(duì)列里面add插入元素會(huì)拋IllegalStateException: Queue full當(dāng)阻塞隊(duì)列空時(shí),再往隊(duì)列Remove元素時(shí)候回拋出NoSuchElementException特殊值插入方法,成功返回true 失敗返回false移除方法,成功返回元素,隊(duì)列里面沒(méi)有就返回null一直阻塞當(dāng)阻塞隊(duì)列滿時(shí),生產(chǎn)者繼續(xù)往隊(duì)列里面put元素,隊(duì)列會(huì)一直阻塞直到put數(shù)據(jù)or響應(yīng)中斷退出當(dāng)阻塞隊(duì)列空時(shí),消費(fèi)者試圖從隊(duì)列take元素,隊(duì)列會(huì)一直阻塞消費(fèi)者線程直到隊(duì)列可用.超時(shí)退出當(dāng)阻塞隊(duì)列滿時(shí),
隊(duì)列會(huì)阻塞生產(chǎn)者線程一定時(shí)間,超過(guò)后限時(shí)后生產(chǎn)者線程就會(huì)退出
?
架構(gòu)梳理+種類(lèi)分析
架構(gòu)介紹
種類(lèi)分析
ArrayBlockingQueue: 由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列.
LinkedBlockingDeque: 由鏈表結(jié)構(gòu)組成的有界(但大小默認(rèn)值Integer>MAX_VALUE)阻塞隊(duì)列.
PriorityBlockingQueue:支持優(yōu)先級(jí)排序的無(wú)界阻塞隊(duì)列.
DelayQueue: 使用優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的延遲無(wú)界阻塞隊(duì)列.
SynchronousQueue:不存儲(chǔ)元素的阻塞隊(duì)列,也即是單個(gè)元素的隊(duì)列.
理論:SynchronousQueue沒(méi)有容量與其他BlcokingQueue不同,SynchronousQueue是一個(gè)不存儲(chǔ)元素的BlcokingQueue每個(gè)put操作必須要等待一個(gè)take操作,否則不能繼續(xù)添加元素,反之亦然. /** * Description * 阻塞隊(duì)列SynchronousQueue演示 * * @author veliger@163.com * @version 1.0 * @date 2019-04-13 13:49 **/ public class SynchronousQueueDemo { public static void main(String[] args) { BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + "\t put 1"); blockingQueue.put("1"); System.out.println(Thread.currentThread().getName() + "\t put 2"); blockingQueue.put("2"); System.out.println(Thread.currentThread().getName() + "\t put 3"); blockingQueue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } }, "AAA").start(); new Thread(() -> { try { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take()); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take()); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }, "BBB").start(); }} SynchronousQueueDemo?
LinkedTransferQueue:由鏈表結(jié)構(gòu)組成的無(wú)界阻塞隊(duì)列.
?
LinkedBlockingDeque:由了解結(jié)構(gòu)組成的雙向阻塞隊(duì)列.
?
用在哪里
生產(chǎn)者消費(fèi)者模式
傳統(tǒng)版
/** * 共享資源類(lèi) */ class ShareData { private int num = 0; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void increment() throws Exception { lock.lock(); try { //判斷 while (num != 0) { //等待 不生產(chǎn) condition.await(); } //干活 num++; System.out.println(Thread.currentThread().getName() + "\t" + num); //通知喚醒 condition.signalAll(); } finally { lock.unlock(); } } public void deIncrement() throws Exception { lock.lock(); try { //判斷 while (num == 0) { //等待 不生產(chǎn) condition.await(); } //干活 num--; System.out.println(Thread.currentThread().getName() + "\t" + num); //通知喚醒 condition.signalAll(); } finally { lock.unlock(); } }}/** * Description * 一個(gè)初始值為0的變量 兩個(gè)線程交替操作 一個(gè)加1 一個(gè)減1來(lái)5輪 * * @author veliger@163.com * @version 1.0 * @date 2019-04-13 14:01 **/ public class ProdConsumerTraditionDemo { public static void main(String[] args) { ShareData shareData = new ShareData(); new Thread(() -> { for (int i = 1; i <= 5; i++) { try { shareData.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "AA").start(); new Thread(() -> { for (int i = 1; i <= 5; i++) { try { shareData.deIncrement(); } catch (Exception e) { e.printStackTrace(); } } }, "BB").start(); }} ProdConsumerTraditionDemo?
阻塞隊(duì)列版
class MyResource { /** * 默認(rèn)開(kāi)啟 進(jìn)行生產(chǎn)消費(fèi)的交互 */ private volatile boolean flag = true; /** * 默認(rèn)值是0 */ private AtomicInteger atomicInteger = new AtomicInteger(); private BlockingQueue<String> blockingQueue = null; public MyResource(BlockingQueue<String> blockingQueue) { this.blockingQueue = blockingQueue; System.out.println(blockingQueue.getClass().getName()); } public void myProd() throws Exception { String data = null; boolean returnValue; while (flag) { data = atomicInteger.incrementAndGet() + ""; returnValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS); if (returnValue) { System.out.println(Thread.currentThread().getName() + "\t 插入隊(duì)列數(shù)據(jù)" + data + "成功"); } else { System.out.println(Thread.currentThread().getName() + "\t 插入隊(duì)列數(shù)據(jù)" + data + "失敗"); } TimeUnit.SECONDS.sleep(1); } System.out.println(Thread.currentThread().getName() + "\t 停止 表示 flag" + flag); } public void myConsumer() throws Exception { String result = null; while (flag) { result = blockingQueue.poll(2L, TimeUnit.SECONDS); if(null==result||"".equalsIgnoreCase(result)){ flag=false; System.out.println(Thread.currentThread().getName()+"\t"+"超過(guò)2m沒(méi)有取到 消費(fèi)退出"); System.out.println(); System.out.println(); return; } System.out.println(Thread.currentThread().getName() + "消費(fèi)隊(duì)列" + result + "成功"); } } public void stop() throws Exception{ flag=false; }}/** * Description * volatile/CAS/atomicInteger/BlockQueue/線程交互/原子引用 * * @author veliger@163.com * @version 1.0 * @date 2019-04-13 14:02 **/ public class ProdConsumerBlockQueueDemo { public static void main(String[] args) throws Exception { MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10)); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t生產(chǎn)線程啟動(dòng)"); try { myResource.myProd(); } catch (Exception e) { e.printStackTrace(); } },"Prod").start(); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t消費(fèi)線程啟動(dòng)"); try { myResource.myConsumer(); } catch (Exception e) { e.printStackTrace(); } },"consumer").start(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(); System.out.println(); System.out.println(); System.out.println("時(shí)間到,停止活動(dòng)"); myResource.stop(); }} ProdConsumerBlockQueueDemo?
線程池
消息中間件
?
線程池? ? ThreadPoolExecutor
為什么使用線程池,優(yōu)勢(shì)
線程池做的工作主要是控制運(yùn)行的線程的數(shù)量,處理過(guò)程中將任務(wù)加入隊(duì)列,然后在線程創(chuàng)建后啟動(dòng)這些任務(wù),如果先生超過(guò)了最大數(shù)量,超出的數(shù)量的線程排隊(duì)等候,等其他線程執(zhí)行完畢,再?gòu)年?duì)列中取出任務(wù)來(lái)執(zhí)行.他的主要特點(diǎn)為:線程復(fù)用:控制最大并發(fā)數(shù):管理線程.
第一:降低資源消耗.通過(guò)重復(fù)利用自己創(chuàng)建的線程降低線程創(chuàng)建和銷(xiāo)毀造成的消耗.
第二: 提高響應(yīng)速度.當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程和粗昂就愛(ài)你就能立即執(zhí)行.
第三: 提高線程的可管理性.線程是稀缺資源,如果無(wú)限的創(chuàng)阿金,不僅會(huì)消耗資源,還會(huì)較低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一分配,調(diào)優(yōu)和監(jiān)控.
?
線程池如何使用?
架構(gòu)實(shí)現(xiàn)
Java中的線程池是通過(guò)Executor框架實(shí)現(xiàn)的,該框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor這幾個(gè)類(lèi).?
編碼實(shí)現(xiàn)
了解
Executors.newCachedThreadPool(); java8新出: Executors.newWorkStealingPool(int); java8新增,使用目前機(jī)器上可以的處理器作為他的并行級(jí)別?
重點(diǎn)
Executors.newFixedThreadPool(int)
主要特點(diǎn)如下:1.創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程的最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待.2.newFixedThreadPool創(chuàng)建的線程池corePoolSize和MaxmumPoolSize是 相等的,它使用的的LinkedBlockingQueue執(zhí)行一個(gè)長(zhǎng)期的任務(wù),性能好很多?
Executors.newSingleThreadExecutor()
主要特點(diǎn)如下:1.創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)都按照指定順序執(zhí)行.2.newSingleThreadExecutor將corePoolSize和MaxmumPoolSize都設(shè)置為1,它使用的的LinkedBlockingQueue一個(gè)任務(wù)一個(gè)線程執(zhí)行的任務(wù)場(chǎng)景?
Executors.newCachedThreadPool()
主要特點(diǎn)如下:1.創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則創(chuàng)建新線程.2.newCachedThreadPool將corePoolSize設(shè)置為0MaxmumPoolSize設(shè)置為Integer.MAX_VALUE,它使用的是SynchronousQUeue,也就是說(shuō)來(lái)了任務(wù)就創(chuàng)建線程運(yùn)行,如果線程空閑超過(guò)60秒,就銷(xiāo)毀線程 適用:執(zhí)行很多短期異步的小程序或者負(fù)載較輕的服務(wù)器?
ThreadPoolExecutor
?
線程池七大重要參數(shù)
1.corePoolSize:線程池中的常駐核心線程數(shù)
1.在創(chuàng)建了線程池后,當(dāng)有請(qǐng)求任務(wù)來(lái)之后,就會(huì)安排池中的線程去執(zhí)行請(qǐng)求任務(wù),近視理解為今日當(dāng)值線程2.當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后,就會(huì)把到達(dá)的任務(wù)放入到緩存隊(duì)列當(dāng)中.?
2.maximumPoolSize:線程池能夠容納同時(shí)執(zhí)行的最大線程數(shù),此值大于等于1
?3.keepAliveTime:多余的空閑線程存活時(shí)間,當(dāng)空間時(shí)間達(dá)到keepAliveTime值時(shí),多余的線程會(huì)被銷(xiāo)毀直到只剩下corePoolSize個(gè)線程為止
默認(rèn)情況下:只有當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí)keepAliveTime才會(huì)起作用,知道線程中的線程數(shù)不大于corepoolSIze,?
4.unit:keepAliveTime的單位
5.workQueue:任務(wù)隊(duì)列,被提交但尚未被執(zhí)行的任務(wù).
6.threadFactory:表示生成線程池中工作線程的線程工廠,用戶(hù)創(chuàng)建新線程,一般用默認(rèn)即可
7.handler:拒絕策略,表示當(dāng)線程隊(duì)列滿了并且工作線程大于等于線程池的最大顯示 數(shù)(maxnumPoolSize)時(shí)如何來(lái)拒絕.
?
線程池的底層工作原理
?
?
?
線程池用過(guò)嗎?生產(chǎn)上你是如何設(shè)置合理參數(shù)
?線程池的拒絕策略請(qǐng)你談?wù)?/h2> 是什么
等待隊(duì)列也已經(jīng)排滿了,再也塞不下新的任務(wù)了同時(shí),線程池的max也到達(dá)了,無(wú)法接續(xù)為新任務(wù)服務(wù)這時(shí)我們需要拒絕策略機(jī)制合理的處理這個(gè)問(wèn)題. JDK內(nèi)置的拒絕策略
AbortPolicy(默認(rèn)):直接拋出RejectedException異常阻止系統(tǒng)正常運(yùn)行CallerRunPolicy:"調(diào)用者運(yùn)行"一種調(diào)節(jié)機(jī)制,該策略既不會(huì)拋棄任務(wù),也不會(huì)拋出異常,而是DiscardOldestPolicy:拋棄隊(duì)列中等待最久的任務(wù),然后把當(dāng)前任務(wù)加入隊(duì)列中嘗試再次提交DiscardPolicy:直接丟棄任務(wù),不予任何處理也不拋出異常.如果允許任務(wù)丟失,這是最好的拒絕策略
?
?以上內(nèi)置策略均實(shí)現(xiàn)了RejectExecutionHandler接口
?你在工作中單一的/固定數(shù)的/可變你的三種創(chuàng)建線程池的方法,你用哪個(gè)多?超級(jí)大坑
答案是一個(gè)都不用,我們生產(chǎn)上只能使用自定義的 參考阿里巴巴java開(kāi)發(fā)手冊(cè) 【強(qiáng)制】線程資源必須通過(guò)線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。 說(shuō)明:使用線程池的好處是減少在創(chuàng)建和銷(xiāo)毀線程上所消耗的時(shí)間以及系統(tǒng)資源的開(kāi)銷(xiāo),解決資源不足的問(wèn)題。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類(lèi)線程而導(dǎo)致消耗完內(nèi)存或者“過(guò)度切換”的問(wèn)題。【強(qiáng)制】線程池不允許使用Executors去創(chuàng)建,而是通過(guò)ThreadPoolExecutor的方式,這樣的處理方式讓寫(xiě)的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。說(shuō)明:Executors返回的線程池對(duì)象的弊端如下:1)FixedThreadPool和SingleThreadPool:允許的請(qǐng)求隊(duì)列長(zhǎng)度為Integer.MAX_VALUE,
可能會(huì)堆積大量的請(qǐng)求,從而導(dǎo)致OOM。2)CachedThreadPool和ScheduledThreadPool:允許的創(chuàng)建線程數(shù)量為Integer.MAX_VALUE,可能會(huì)創(chuàng)建大量的線程,從而導(dǎo)致OOM。
?
你在工作中是如何創(chuàng)建線程池的,是否自定義過(guò)線程池使用
public class MyThreadPoolDemo { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 1L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(3), Executors.defaultThreadFactory(), //默認(rèn)拋出異常 //new ThreadPoolExecutor.AbortPolicy() //回退調(diào)用者 //new ThreadPoolExecutor.CallerRunsPolicy() //處理不來(lái)的不處理 //new ThreadPoolExecutor.DiscardOldestPolicy() new ThreadPoolExecutor.DiscardPolicy() ); //模擬10個(gè)用戶(hù)來(lái)辦理業(yè)務(wù) 沒(méi)有用戶(hù)就是來(lái)自外部的請(qǐng)求線程. try { for (int i = 1; i <= 10; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 辦理業(yè)務(wù)"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } //threadPoolInit(); } private static void threadPoolInit() { /** * 一池5個(gè)處理線程 */ //ExecutorService threadPool= Executors.newFixedThreadPool(5); /** * 一池一線程 */ //ExecutorService threadPool= Executors.newSingleThreadExecutor(); /** * 一池N線程 */ ExecutorService threadPool = Executors.newCachedThreadPool(); //模擬10個(gè)用戶(hù)來(lái)辦理業(yè)務(wù) 沒(méi)有用戶(hù)就是來(lái)自外部的請(qǐng)求線程. try { for (int i = 1; i <= 20; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 辦理業(yè)務(wù)"); }); try { TimeUnit.MICROSECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } }} Case?
?合理配置線程池你是如何考慮的?
CPU密集型?
System.out.println(Runtime.getRuntime().availableProcessors());查看CPU核數(shù)?
?IO密集型
死鎖編碼及定位分析?
是什么
產(chǎn)生死鎖的主要原因
系統(tǒng)資源不足 進(jìn)程運(yùn)行推進(jìn)的順序不合適 資源分配不當(dāng)代碼
class HoldThread implements Runnable { private String lockA; private String lockB; public HoldThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "\t 自己持有鎖" + lockA + "嘗試獲得" + lockB); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "\t 自己持有鎖" + lockB + "嘗試獲得" + lockA); } } }}/** * Description: * 死鎖是指兩個(gè)或者以上的進(jìn)程在執(zhí)行過(guò)程中, * 因爭(zhēng)奪資源而造成的一種相互等待的現(xiàn)象, * 若無(wú)外力干涉那他們都將無(wú)法推進(jìn)下去 * * @author veliger@163.com * @date 2019-04-14 0:05 **/ public class DeadLockDemo { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new HoldThread(lockA, lockB), "threadAAA").start(); new Thread(new HoldThread(lockB, lockA), "threadBBB").start(); }} View Code?
解決
jps命令定位進(jìn)程編號(hào) jstack找到死鎖查看?
Java里面鎖請(qǐng)談?wù)勀愕睦斫饽苷f(shuō)多少說(shuō)多少
?
轉(zhuǎn)載于:https://www.cnblogs.com/biaogejiushibiao/p/11269179.html
總結(jié)
以上是生活随笔為你收集整理的互联网大厂高频重点面试题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Silverlight学习(一) 创建S
- 下一篇: 【CF1063B】Labyrinth [