1锋勺、并行與并發(fā)的區(qū)別
1)并行指多個事件在同一個時刻發(fā)生;并發(fā)指在某時刻只有一個事件在發(fā)生狡蝶,某個時間段內(nèi)由于 CPU 交替執(zhí)行庶橱,可以發(fā)生多個事件。
2)并行沒有對 CPU 資源的搶占贪惹;并發(fā)執(zhí)行的線程需要對CPU 資源進(jìn)行搶占苏章。
3)并行執(zhí)行的線程之間不存在切換;并發(fā)操作系統(tǒng)會根據(jù)任務(wù)調(diào)度系統(tǒng)給線程分配線程的 CPU 執(zhí)行時間奏瞬,線程的執(zhí)行會進(jìn)行切換枫绅。
2、什么是并發(fā)編程硼端?
并發(fā):
1)在程序設(shè)計(jì)的角度并淋,希望通過某些機(jī)制讓計(jì)算機(jī)可以在一個時間段內(nèi),執(zhí)行多個任務(wù)珍昨。
2)一個或多個物理 CPU 在多個程序之間多路復(fù)用县耽,提高對計(jì)算機(jī)資源的利用率。
3)任務(wù)數(shù)多余 CPU 的核數(shù)镣典,通過操作系統(tǒng)的任務(wù)調(diào)度算法兔毙,實(shí)現(xiàn)多個任務(wù)一起執(zhí)行。
4)有多個線程在執(zhí)行兄春,計(jì)算機(jī)只有一個 CPU澎剥,不可能真正同時運(yùn)行多個線程,操作系統(tǒng)只能把 CPU 運(yùn)行時間劃分成若干個時間段赶舆,再將時間段分配給各個線程執(zhí)行肴裙,在一個時間段的線程代碼運(yùn)行時,其它線程處于掛起狀涌乳。
并發(fā)編程:用編程語言編寫讓計(jì)算機(jī)可以在一個時間段內(nèi)執(zhí)行多個任務(wù)的程序蜻懦。
3、為什么要用并發(fā)編程夕晓?
1)"摩爾定律" 失效宛乃,硬件的單元計(jì)算能力提升受限;硬件上提高了 CPU 的核數(shù)和個數(shù)。并發(fā)編程可以提升 CPU 的計(jì)算能力的利用率征炼;
2)提升程序的性能析既,如:響應(yīng)時間、吞吐量谆奥、計(jì)算機(jī)資源使用率等眼坏;
3)并發(fā)程序可以更好地處理復(fù)雜業(yè)務(wù),對復(fù)雜業(yè)務(wù)進(jìn)行多任務(wù)拆分酸些,簡化任務(wù)調(diào)度宰译,同步執(zhí)行任務(wù)。
4魄懂、并發(fā)編程的缺點(diǎn)沿侈?
1)Java 中的線程對應(yīng)是操作系統(tǒng)級別的線程,線程數(shù)量控制不好市栗,頻繁的創(chuàng)建缀拭、銷毀線程和線程間的切換,比較消耗內(nèi)存和時間填帽。
2)容易帶來線程安全問題蛛淋。如線程的可見性、有序性篡腌、原子性問題铣鹏,會導(dǎo)致程序出現(xiàn)的結(jié)果與預(yù)期結(jié)果不一致。
3)多線程容易造成死鎖哀蘑、活鎖诚卸、線程饑餓等問題。此類問題往往只能通過手動停止線程绘迁、甚至是進(jìn)程才能解決合溺,影響嚴(yán)重。
4)對編程人員的技術(shù)要求較高缀台,編寫出正確的并發(fā)程序并不容易棠赛。
5)并發(fā)程序易出問題,且難調(diào)試和排查膛腐;問題常常詭異地出現(xiàn)睛约,又詭異地消失。
5哲身、導(dǎo)致并發(fā)程序出問題的根本原因是什么辩涝?
CPU、內(nèi)存勘天、IO 設(shè)備的讀寫速度差異巨大怔揩,表現(xiàn)為 CPU 的速度 > 內(nèi)存的速度 > IO 設(shè)備的速度捉邢。程序的性能瓶頸在于速度最慢的 IO 設(shè)備的讀寫,也就是說當(dāng)涉及到 IO 設(shè)備的讀寫商膊,再怎么提升 CPU 和內(nèi)存的速度也是起不到提升性能的作用伏伐。
為了更好地利用 CPU 的高性能:
1)計(jì)算機(jī)體系結(jié)構(gòu),給 CPU 增加了緩存晕拆,均衡 CPU 和內(nèi)存的速度差異藐翎;
2)操作系統(tǒng),增加了進(jìn)程與線程实幕,分時復(fù)用 CPU吝镣,均衡 CPU 和 IO 設(shè)備的速度差異;
3)編譯器茬缩,增加了指令執(zhí)行重排序赤惊,更好地利用緩存吼旧,提高程序的執(zhí)行速度凰锡。
基于以上優(yōu)化,給并發(fā)編程帶來了三大問題圈暗。
1掂为、 CPU 緩存,在多核 CPU 的情況下员串,帶來了可見性問題
可見性:一個線程對共享變量的修改勇哗,另一個線程能夠立刻看到修改后的值;
2寸齐、操作系統(tǒng)對當(dāng)前執(zhí)行線程的切換欲诺,帶來了原子性問題
原子性:一個或多個指令在 CPU 執(zhí)行的過程中不被中斷的特性;
3渺鹦、編譯器指令重排優(yōu)化扰法,帶來了有序性問題
有序性:程序按照代碼執(zhí)行的先后順序。
6毅厚、同步和異步的區(qū)別塞颁?
同步和異步關(guān)注的是消息通信機(jī)制。
同步:就是在發(fā)出一個調(diào)用時吸耿,在沒有得到結(jié)果之前祠锣,該調(diào)用就不返回。但是一旦調(diào)用返回咽安,就得到返回值了伴网。
異步:當(dāng)一個異步調(diào)用發(fā)出后,這個調(diào)用就直接返回了妆棒,所以沒有返回結(jié)果是偷。換句話說拳氢,當(dāng)一個異步過程調(diào)用發(fā)出后,調(diào)用者不會立刻得到結(jié)果蛋铆。而是在調(diào)用發(fā)出后馋评,被調(diào)用者通過狀態(tài)、通知來通知調(diào)用者刺啦,或通過回調(diào)函數(shù)處理這個調(diào)用留特。
舉例子的話:去銀行辦理業(yè)務(wù),可能會有兩種方式:
1玛瘸、客戶排隊(duì)等候蜕青;
2、每個人都有一個號碼牌糊渊,然后機(jī)器叫號讓客戶辦理業(yè)務(wù)右核。
第一種就是同步,(排隊(duì)等候)就是同步等待消息通知渺绒,也就是客戶要一直在等待銀行辦理業(yè)務(wù)情況贺喝。
第二種:(等待機(jī)器叫號通知)就是異步等待消息通知。在異步消息處理中宗兼,等待消息通知者往往注冊一個回調(diào)機(jī)制躏鱼,在所等待的事件被觸發(fā)時由觸發(fā)機(jī)制(機(jī)器叫號)通過某種機(jī)制找到等待該事件的人。
7殷绍、阻塞與非阻塞的區(qū)別染苛?
阻塞和非阻塞關(guān)注的是程序在等待調(diào)用結(jié)果(消息,返回值)時的狀態(tài)主到。
阻塞調(diào)用是指調(diào)用結(jié)果返回之前茶行,當(dāng)前線程會被掛起。調(diào)用線程只有在得到結(jié)果之后才會返回登钥。
非阻塞調(diào)用指在不能立刻得到結(jié)果之前畔师,該調(diào)用不會阻塞當(dāng)前線程。
還是上面的例子怔鳖,去銀行辦理業(yè)務(wù)茉唉,如果是阻塞式調(diào)用的話,你會把自己“掛起”结执,一直等待輪到自己度陆,但是如果是非阻塞式調(diào)用的話,你不用等待是否輪到自己献幔,你可以先干自己的事情懂傀,等到機(jī)器叫號叫到你就可以了。
8蜡感、Java程序中如何保證多線程的運(yùn)行安全蹬蚁?
線程的安全性問題體現(xiàn)在:
1)原子性:一個或者多個操作在 CPU 執(zhí)行的過程中不被中斷的特性
2)可見性:一個線程對共享變量的修改恃泪,另外一個線程能夠立刻看到
3)有序性:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行
導(dǎo)致原因:
1)緩存導(dǎo)致的可見性問題
2)線程切換帶來的原子性問題
3)編譯優(yōu)化帶來的有序性問題
解決辦法:
1)JDK Atomic開頭的原子類、synchronized犀斋、LOCK贝乎,可以解決原子性問題
2)synchronized、volatile叽粹、LOCK览效,可以解決可見性問題
3)Happens-Before 規(guī)則可以解決有序性問題。
9虫几、什么是守護(hù)線程锤灿?
Java線程分為用戶線程和守護(hù)線程。
1)守護(hù)線程是程序運(yùn)行的時候在后臺提供一種通用服務(wù)的線程辆脸。所有用戶線程停止但校,進(jìn)程會停掉所有守護(hù)線程,退出程序啡氢,意思就是只要有一個非守護(hù)線程仍在工作状囱,那么所有的守護(hù)線程就會一直工作;
2)Java中把線程設(shè)置為守護(hù)線程的方法:在 start 線程之前調(diào)用線程的 setDaemon(true) 方法空执。
注意:
1)setDaemon(true) 必須在 start() 之前設(shè)置浪箭,否則會拋出IllegalThreadStateException異常穗椅,該線程仍默認(rèn)為用戶線程辨绊,繼續(xù)執(zhí)行守護(hù)線程創(chuàng)建的線程也是守護(hù)線程;
2)守護(hù)線程不應(yīng)該訪問匹表、寫入持久化資源门坷,如文件、數(shù)據(jù)庫袍镀,因?yàn)樗鼤谌魏螘r間被停止默蚌,導(dǎo)致資源未釋放、數(shù)據(jù)寫入中斷等問題苇羡。
10绸吸、創(chuàng)建線程的方式有哪幾種?
1)繼承Thread類设江,重寫run方法
public class TestExtThread {
public static void main(String[] args) {
new ThreadExt().start();
}
}
class ThreadExt extends Thread {
@Override
public void run() {
for (int i = 0; i <3; i++) {
System.out.println("thread t > " + i);
}
}
}
2)實(shí)現(xiàn)Runnable接口锦茁,重寫run()方法
public class TestImplRunnable {
public static void main(String[] args) {
new Thread(new RunnableImpl()).start();
}
}
class RunnableImpl implements Runnable {
@Override
public void run() {
for (int i = 0; i <3; i++) {
System.out.println("thread t > " + i);
}
}
}
3)實(shí)現(xiàn)Callable接口,使用FutureTask類創(chuàng)建線程(可以實(shí)現(xiàn)有返回值的線程創(chuàng)建叉存,并且可以拋出異常)
public class TestCallable {
public static void main(String[] args) {
CallableDemo cd = new CallableDemo();
//使用Callable方式創(chuàng)建線程码俩,需要FutureTask類的支持
//用于接收運(yùn)算結(jié)果,可以使用泛型指定返回值的類型
FutureTask<Integer> result = new FutureTask<>(cd);
new Thread(result).start();
int sum = 0;
// 接收運(yùn)算結(jié)果
// 只有當(dāng)該線程執(zhí)行完畢后才會獲取到運(yùn)算結(jié)果歼捏,等同于閉鎖的效果
try {
sum = result.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("sum is " + sum);
}
}
class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 計(jì)算1-100的和
int sum = 0;
for (int i = 1; i <= 100; i++)
sum += i;
return sum;
}
4)使用線程池創(chuàng)建線程
public class TestCreateThreadByThreadPool {
public static void main(String[] args) {
// 使用工具類 Executors 創(chuàng)建單線程線程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//提交執(zhí)行任務(wù)
singleThreadExecutor.submit(() -> {System.out.println("單線程線程池執(zhí)行任務(wù)");});
//關(guān)閉線程池
singleThreadExecutor.shutdown();
}
}
11稿存、什么是線程池笨篷?
線程池就是創(chuàng)建若干個可執(zhí)行的線程放入一個池(容器)中,有任務(wù)需要處理時瓣履,會提交到線程池中的任務(wù)隊(duì)列率翅,處理完之后線程并不會被銷毀,而是仍然在線程池中等待下一個任務(wù)袖迎,避免了創(chuàng)建和銷毀線程的昂貴開銷安聘,因?yàn)?Java 中創(chuàng)建一個線程,需要調(diào)用操作系統(tǒng)內(nèi)核的 API瓢棒,操作系統(tǒng)要為線程分配一系列的資源浴韭,成本很高,所以線程是一個重量級的對象脯宿,應(yīng)該避免頻繁創(chuàng)建和銷毀念颈。而使用線程池就能起到對線程重復(fù)利用的效果,節(jié)省資源開銷连霉,使得性能大大提升榴芳。
12、線程池有哪幾種類型跺撼?
1)newFixedThreadPool(固定線程數(shù)的線程池)
作用:創(chuàng)建一個指定工作線程數(shù)量的線程池窟感。每當(dāng)提交一個任務(wù)就創(chuàng)建一個工作線程,如果工作線程數(shù)量達(dá)到線程池初始的最大數(shù)歉井,則將提交的任務(wù)存入到池隊(duì)列中柿祈。
特征:
(1)線程池中的線程處于一定的量,可以很好的控制線程的并發(fā)量
(2)線程可以重復(fù)被使用哩至,在顯示關(guān)閉之前躏嚎,都將一直存在
(3)超出一定量的線程被提交時候需在隊(duì)列中等待
2)newCachedThreadPool(可緩存線程池)
作用:創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要菩貌,可靈活回收空閑線程卢佣,若無可回收,則新建線程箭阶。
特征:
(1)線程池中數(shù)量沒有固定虚茶,可達(dá)到最大值(Interger. MAX_VALUE)
(2)線程池中的線程可進(jìn)行緩存重復(fù)利用和回收(回收默認(rèn)時間為1分鐘)
(3)當(dāng)線程池中,沒有可用線程仇参,會重新創(chuàng)建一個線程
3)newSingleThreadExecutor(單線程的線程池)
作用:創(chuàng)建一個單線程化的Executor嘹叫,即只創(chuàng)建唯一的工作者線程來執(zhí)行任務(wù),它只會用唯一的工作線程來執(zhí)行任務(wù)冈敛,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行待笑。如果這個線程異常結(jié)束,會有另一個取代它抓谴,保證順序執(zhí)行暮蹂。單工作線程最大的特點(diǎn)是可保證順序地執(zhí)行各個任務(wù)寞缝,并且在任意給定的時間不會有多個線程是活動的。
特征:
(1)線程池中最多執(zhí)行1個線程仰泻,之后提交的線程活動將會排在隊(duì)列中等待執(zhí)行荆陆。
4)newScheduleThreadPool(延時線程池)
作用: 創(chuàng)建一個線程池,它可安排在給定延遲后運(yùn)行命令或者定期地執(zhí)行集侯。
特征:
(1)線程池中具有指定數(shù)量的線程被啼,即便是空線程也將保留
(2)可定時或者延遲執(zhí)行線程活動
5)newSingleThreadScheduledExecutor(單線程的延時線程池)
作用: 創(chuàng)建一個單線程執(zhí)行程序,它可安排在給定延遲后運(yùn)行命令或者定期地執(zhí)行棠枉。
特征:
(1)線程池中最多執(zhí)行1個線程浓体,之后提交的線程活動將會排在隊(duì)列中以此執(zhí)行
(2)可定時或者延遲執(zhí)行線程活動
13、線程池的四種拒絕策略是什么辈讶?
線程池中命浴,有三個重要的參數(shù),決定影響了拒絕策略:corePoolSize - 核心線程數(shù)贱除,也即最小的線程數(shù)生闲,workQueue - 阻塞隊(duì)列 。maximumPoolSize - 最大線程數(shù)月幌。當(dāng)提交任務(wù)數(shù)大于 corePoolSize 的時候碍讯,會優(yōu)先將任務(wù)放到 workQueue 阻塞隊(duì)列中。當(dāng)阻塞隊(duì)列飽和后扯躺,會擴(kuò)充線程池中線程數(shù)捉兴,直到達(dá)到 maximumPoolSize 最大線程數(shù)配置。此時缅帘,再多余的任務(wù)轴术,則會觸發(fā)線程池的拒絕策略了难衰。
總結(jié)起來钦无,也就是一句話,當(dāng)提交的任務(wù)數(shù)大于(workQueue.size() + maximumPoolSize )盖袭,就會觸發(fā)線程池的拒絕策略失暂。
1)CallerRunsPolicy -- 當(dāng)觸發(fā)拒絕策略,只要線程池沒有關(guān)閉的話鳄虱,則使用調(diào)用線程直接運(yùn)行任務(wù)弟塞。一般并發(fā)比較小,性能要求不高拙已,不允許失敗决记。但是,由于調(diào)用者自己運(yùn)行任務(wù)倍踪,如果任務(wù)提交速度過快系宫,可能導(dǎo)致程序阻塞索昂,性能效率上必然的損失較大;
2)AbortPolicy -- 丟棄任務(wù)扩借,并拋出拒絕執(zhí)行 RejectedExecutionException 異常信息椒惨。線程池默認(rèn)的拒絕策略。必須處理好拋出的異常潮罪,否則會打斷當(dāng)前的執(zhí)行流程康谆,影響后續(xù)的任務(wù)執(zhí)行;
3)DiscardPolicy -- 直接丟棄嫉到,其他啥都沒有沃暗;
4)DiscardOldestPolicy -- 當(dāng)觸發(fā)拒絕策略,只要線程池沒有關(guān)閉的話何恶,丟棄阻塞隊(duì)列 workQueue 中最老的一個任務(wù)描睦,并將新任務(wù)加入。
14导而、synchronized關(guān)鍵字的作用是什么忱叭?
Java 中關(guān)鍵字synchronized 表示只有一個線程可以獲取作用對象的鎖,執(zhí)行代碼今艺,阻塞其他線程韵丑。
作用:
1)確保線程互斥地訪問同步代碼
2)保證共享變量的修改能夠及時可見
3)有效解決重排序問題
用法:
1)修飾普通方法
2)修飾靜態(tài)方法
3)指定對象,修飾代碼塊
特點(diǎn):
1)阻塞未獲取到鎖虚缎、競爭同一個對象鎖的線程
2)獲取鎖無法設(shè)置超時
3)無法實(shí)現(xiàn)公平鎖
4)控制等待和喚醒需要結(jié)合加鎖對象的 wait() 和 notify()撵彻、notifyAll()
5)鎖的功能是 JVM 層面實(shí)現(xiàn)的
6)在加鎖代碼塊執(zhí)行完或者出現(xiàn)異常,自動釋放鎖
原理:
1)同步代碼塊是通過 monitorenter 和 monitorexit 指令獲取線程的執(zhí)行權(quán)
2)同步方法通過加 ACC_SYNCHRONIZED 標(biāo)識實(shí)現(xiàn)線程的執(zhí)行權(quán)的控制
15实牡、volatile關(guān)鍵字的作用是什么陌僵?
Java 中 volatile 關(guān)鍵字是一個類型修飾符。JDK 1.5 之后创坞,對其語義進(jìn)行了增強(qiáng)碗短。
1)保證了不同線程對共享變量進(jìn)行操作時的可見性,即一個線程修改了共享變量的值题涨,共享變量修改后的值對其他線程立即可見偎谁;
2)通過禁止編譯器、CPU 指令重排序和部分 happens-before 規(guī)則纲堵,解決有序性問題巡雨;
volatile 可見性的實(shí)現(xiàn):
1)在生成匯編代碼指令時會在 volatile 修飾的共享變量進(jìn)行寫操作的時候會多出 Lock 前綴的指令
2)Lock 前綴的指令會引起 CPU 緩存寫回內(nèi)存
3)一個 CPU 的緩存回寫到內(nèi)存會導(dǎo)致其他 CPU 緩存了該內(nèi)存地址的數(shù)據(jù)無效
4)volatile 變量通過緩存一致性協(xié)議保證每個線程獲得最新值
5)緩存一致性協(xié)議保證每個 CPU 通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己緩存的值是不是修改
6)當(dāng) CPU 發(fā)現(xiàn)自己緩存行對應(yīng)的內(nèi)存地址被修改,會將當(dāng)前 CPU 的緩存行設(shè)置成無效狀態(tài)席函,重新從內(nèi)存中把數(shù)據(jù)讀到 CPU 緩存铐望。
16、Java中的鎖是什么?
在并發(fā)編程中正蛙,經(jīng)常會遇到多個線程訪問同一個共享變量炕舵,當(dāng)同時對共享變量進(jìn)行讀寫操作時,就會產(chǎn)生數(shù)據(jù)不一致的情況跟畅。
為了解決這個問題
1)JDK 1.5 之前咽筋,使用 synchronized 關(guān)鍵字,拿到 Java 對象的鎖徊件,保護(hù)鎖定的代碼塊奸攻。JVM 保證同一時刻只有一個線程可以拿到這個 Java 對象的鎖,執(zhí)行對應(yīng)的代碼塊虱痕。
2)JDK 1.5 開始睹耐,引入了并發(fā)工具包 java.util.concurrent.locks.Lock,讓鎖的功能更加豐富部翘。
常見的鎖
1)synchronized 關(guān)鍵字鎖定代碼庫
2)可重入鎖 java.util.concurrent.lock.ReentrantLock
3)可重復(fù)讀寫鎖 java.util.concurrent.lock.ReentrantReadWriteLock
17前翎、Java 中不同維度的鎖分類
可重入鎖
1)指在同一個線程在外層方法獲取鎖的時候亡脸,進(jìn)入內(nèi)層方法會自動獲取鎖崭别。JDK 中基本都是可重入鎖猪叙,避免死鎖的發(fā)生。
公平鎖 / 非公平鎖
1)公平鎖夹囚,指多個線程按照申請鎖的順序來獲取鎖纵刘。如 java.util.concurrent.lock.ReentrantLock.FairSync
2)非公平鎖,指多個線程獲取鎖的順序并不是按照申請鎖的順序荸哟,有可能后申請的線程先獲得鎖假哎。如 synchronized、java.util.concurrent.lock.ReentrantLock.NonfairSync
獨(dú)享鎖 / 共享鎖
1)獨(dú)享鎖鞍历,指鎖一次只能被一個線程所持有舵抹。synchronized、java.util.concurrent.locks.ReentrantLock 都是獨(dú)享鎖
2)共享鎖劣砍,指鎖可被多個線程所持有惧蛹。ReadWriteLock 返回的 ReadLock 就是共享鎖
悲觀鎖 / 樂觀鎖
1)悲觀鎖,一律會對代碼塊進(jìn)行加鎖秆剪,如 synchronized赊淑、java.util.concurrent.locks.ReentrantLock
2)樂觀鎖,默認(rèn)不會進(jìn)行并發(fā)修改仅讽,通常采用 CAS 算法不斷嘗試更新
3)悲觀鎖適合寫操作較多的場景,樂觀鎖適合讀操作較多的場景
粗粒度鎖 / 細(xì)粒度鎖
1)粗粒度鎖钾挟,就是把執(zhí)行的代碼塊都鎖定
2)細(xì)粒度鎖洁灵,就是鎖住盡可能小的代碼塊,java.util.concurrent.ConcurrentHashMap 中的分段鎖就是一種細(xì)粒度鎖
3)粗粒度鎖和細(xì)粒度鎖是相對的,沒有什么標(biāo)準(zhǔn)
偏向鎖 / 輕量級鎖 / 重量級鎖
1)JDK 1.5 之后新增鎖的升級機(jī)制徽千,提升性能苫费。
2)通過 synchronized 加鎖后,一段同步代碼一直被同一個線程所訪問双抽,那么該線程獲取的就是偏向鎖
3)偏向鎖被一個其他線程訪問時百框,Java 對象的偏向鎖就會升級為輕量級鎖
4)再有其他線程會以自旋的形式嘗試獲取鎖,不會阻塞牍汹,自旋一定次數(shù)仍然未獲取到鎖铐维,就會膨脹為重量級鎖
自旋鎖
1)自旋鎖是指嘗試獲取鎖的線程不會立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖慎菲,這樣做的好處是減少線程上下文切換的消耗嫁蛇,缺點(diǎn)是循環(huán)占有、浪費(fèi) CPU 資源露该。
18睬棚、synchronized 和 java.util.concurrent.lock.Lock 之間的區(qū)別
1)實(shí)現(xiàn)層面不一樣。synchronized 是 Java 關(guān)鍵字解幼,JVM層面 實(shí)現(xiàn)加鎖和釋放鎖抑党;Lock 是一個接口,在代碼層面實(shí)現(xiàn)加鎖和釋放鎖
2)是否自動釋放鎖撵摆。synchronized 在線程代碼執(zhí)行完或出現(xiàn)異常時自動釋放鎖新荤;Lock 不會自動釋放鎖,需要在 finally {} 代碼塊顯式地中釋放鎖
3)是否一直等待台汇。synchronized 會導(dǎo)致線程拿不到鎖一直等待苛骨;Lock 可以設(shè)置嘗試獲取鎖或者獲取鎖失敗一定時間超時
4)獲取鎖成功是否可知。synchronized 無法得知是否獲取鎖成功苟呐;Lock 可以通過 tryLock 獲得加鎖是否成功
5)功能復(fù)雜性痒芝。synchronized 加鎖可重入、不可中斷牵素、非公平严衬;Lock 可重入、可判斷笆呆、可公平和不公平请琳、細(xì)分讀寫鎖提高效率。
19赠幕、說說對于sychronized同步鎖的理解
1)每個 Java 對象都有一個內(nèi)置鎖
2)線程運(yùn)行到非靜態(tài)的 synchronized 同步方法上時俄精,自動獲得實(shí)例對象的鎖
3)持有對象鎖的線程才能運(yùn)行 synchronized 同步方法或代碼塊時
4)一個對象只有一個鎖
5)一個線程獲得該鎖,其他線程就無法獲得鎖榕堰,直到第一個線程釋放鎖竖慧。任何其他線程都不能進(jìn)入該對象上的 synchronized 方法或代碼塊嫌套,直到該鎖被釋放。
6)釋放鎖是指持鎖線程退出了 synchronized 同步方法或代碼塊
7)類可以同時擁有同步和非同步方法
8)只有同步方法圾旨,沒有同步變量和類
9)在加鎖時踱讨,要明確需要加鎖的對象
10)線程可以獲得多個鎖
11)同步應(yīng)該盡量縮小范圍
20、sleep()和wait()有什么區(qū)別砍的?
1)sleep() 是 Thread 類的靜態(tài)本地方法痹筛;wait() 是Object類的成員本地方法;
2)sleep() 方法可以在任何地方使用廓鞠;wait() 方法則只能在同步方法或同步代碼塊中使用帚稠,否則拋出異常Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
3)sleep() 會休眠當(dāng)前線程指定時間,釋放 CPU 資源诫惭,不釋放對象鎖翁锡,休眠時間到,會自動蘇醒繼續(xù)執(zhí)行夕土;wait() 方法放棄持有的對象鎖馆衔,進(jìn)入等待隊(duì)列,當(dāng)該對象被調(diào)用 notify() / notifyAll() 方法后才有機(jī)會競爭獲取對象鎖怨绣,進(jìn)入運(yùn)行狀態(tài)角溃;
4)JDK1.8 sleep() 、wait() 均需要捕獲 InterruptedException 異常篮撑。
21减细、notify()和notifyAll()有什么區(qū)別?
先解釋兩個概念赢笨。
1)等待池:假設(shè)一個線程A調(diào)用了某個對象的wait()方法未蝌,線程A就會釋放該對象的鎖后,進(jìn)入到了該對象的等待池茧妒,等待池中的線程不會去競爭該對象的鎖萧吠。
2)鎖池:只有獲取了對象的鎖,線程才能執(zhí)行對象的 synchronized 代碼桐筏,對象的鎖每次只有一個線程可以獲得纸型,其他線程只能在鎖池中等待
區(qū)別:
notify() 方法隨機(jī)喚醒對象的等待池中的一個線程,進(jìn)入鎖池梅忌;notifyAll() 喚醒對象的等待池中的所有線程狰腌,進(jìn)入鎖池。
22牧氮、什么是死鎖琼腔?
線程死鎖是指由于兩個或者多個線程互相持有所需要的資源,導(dǎo)致這些線程一直處于等待其他線程釋放資源的狀態(tài)蹋笼,無法前往執(zhí)行展姐,如果線程都不主動釋放所占有的資源躁垛,將產(chǎn)生死鎖剖毯。
當(dāng)線程處于這種僵持狀態(tài)時圾笨,若無外力作用,它們都將無法再向前推進(jìn)逊谋。
產(chǎn)生原因:
1)持有系統(tǒng)不可剝奪資源擂达,去競爭其他已被占用的系統(tǒng)不可剝奪資源,形成程序僵死的競爭關(guān)系胶滋。
2)持有資源的鎖板鬓,去競爭鎖已被占用的其他資源,形成程序僵死的爭關(guān)系究恤。
3)信號量使用不當(dāng)俭令。
23、樂觀鎖和悲觀鎖的區(qū)別部宿?
悲觀鎖(Pessimistic Lock):線程每次在處理共享數(shù)據(jù)時都會上鎖抄腔,其他線程想處理數(shù)據(jù)就會阻塞直到獲得鎖。
樂觀鎖(Optimistic Lock):線程每次在處理共享數(shù)據(jù)時都不會上鎖理张,在更新時會通過數(shù)據(jù)的版本號等機(jī)制判斷其他線程有沒有更新數(shù)據(jù)赫蛇。樂觀鎖適合讀多寫少的應(yīng)用場景
兩種鎖各有優(yōu)缺點(diǎn):
1)樂觀鎖適用于讀多寫少的場景,可以省去頻繁加鎖雾叭、釋放鎖的開銷悟耘,提高吞吐量
2)在寫比較多的場景下,樂觀鎖會因?yàn)榘姹静灰恢轮粩嘀卦嚫略萦祝a(chǎn)生大量自旋,消耗 CPU移迫,影響性能旺嬉。這種情況下,適合悲觀鎖起意。
24鹰服、Java中實(shí)現(xiàn)線程通訊方式有哪些?
1)對象的 wait(long timeout)揽咕、wait(long timeout, int nanos)悲酷、wait() 方法,組合對象的 notify()亲善、notifyAll()
2)顯示鎖:Lock.newCondition()设易、Condition await 系列方法、Condition signal()蛹头、signalAll()
3)信號量:Semaphore acquire 系列方法顿肺、release()系列方法
25戏溺、synchronized和volatile的區(qū)別是什么?
作用:
1)synchronized 表示只有一個線程可以獲取作用對象的鎖屠尊,執(zhí)行代碼旷祸,阻塞其他線程。
2)volatile 表示變量在 CPU 的寄存器中是不確定的讼昆,必須從主存中讀取托享。保證多線程環(huán)境下變量的可見性;禁止指令重排序浸赫。
區(qū)別:
1)synchronized 可以作用于變量闰围、方法、對象既峡;volatile 只能作用于變量羡榴。
2)synchronized 可以保證線程間的有序性(猜測是無法保證線程內(nèi)的有序性,即線程內(nèi)的代碼可能被 CPU 指令重排序)运敢、原子性和可見性校仑;volatile 只保證了可見性和有序性,無法保證原子性者冤。
3)synchronized 線程阻塞肤视,volatile 線程不阻塞。
26涉枫、synchronized和ReentrantLock區(qū)別是什么邢滑?
1)synchronized 競爭鎖時會一直等待;ReentrantLock 可以嘗試獲取鎖愿汰,并得到獲取結(jié)果
2)synchronized 獲取鎖無法設(shè)置超時困后;ReentrantLock 可以設(shè)置獲取鎖的超時時間
3)synchronized 無法實(shí)現(xiàn)公平鎖;ReentrantLock 可以滿足公平鎖衬廷,即先等待先獲取到鎖
4)synchronized 控制等待和喚醒需要結(jié)合加鎖對象的 wait() 和 notify()摇予、notifyAll();ReentrantLock 控制等待和喚醒需要結(jié)合 Condition 的 await() 和 signal()吗跋、signalAll() 方法
5)synchronized 是 JVM 層面實(shí)現(xiàn)的侧戴;ReentrantLock 是 JDK 代碼層面實(shí)現(xiàn)
6)synchronized 在加鎖代碼塊執(zhí)行完或者出現(xiàn)異常,自動釋放鎖跌宛;ReentrantLock 不會自動釋放鎖酗宋,需要在 finally{} 代碼塊顯示釋放。
27疆拘、Thread類中的start和run方法的區(qū)別?
1)啟動一個線程需要調(diào)用Thread對象的start()方法蜕猫;
2)調(diào)用線程的start()后,線程處于可運(yùn)行狀態(tài)哎迄,此時它可由JVM調(diào)度并執(zhí)行回右,這并不意味著線程就會立即運(yùn)行隆圆;
3)run()方法是線程運(yùn)行時由JVM回調(diào)的方法,無需手動寫代碼調(diào)用翔烁;
4)直接調(diào)用線程的run()方法渺氧,相當(dāng)于調(diào)用線程里繼續(xù)調(diào)用方法,并未啟動一個新的線程租漂。
28阶女、Java內(nèi)存模型是什么颊糜?
Java內(nèi)存模型即Java Memory Model(JMM)哩治。
JMM并不真實(shí)存在,而只是一個抽象的概念衬鱼。JMM是和多線程相關(guān)的业筏,更準(zhǔn)確來說,JMM描述了一組規(guī)則或規(guī)范鸟赫,這個規(guī)范定義了一個線程對共享變量的寫入時對另一個線程是可見的蒜胖。
Java的多線程之間是通過共享內(nèi)存進(jìn)行通信的,而由于采用共享內(nèi)存進(jìn)行通信抛蚤,在通信過程中會存在一系列如可見性台谢、原子性、順序性等問題岁经,而JMM就是圍繞著多線程通信以及與其相關(guān)的一系列特性而建立的模型朋沮。JMM定義了一些語法集,這些語法集映射到Java語言中就是volatile缀壤、synchronized等關(guān)鍵字樊拓。
在JMM中,我們把多個線程間通信的共享內(nèi)存稱之為主內(nèi)存塘慕,而在并發(fā)編程中多個線程都維護(hù)了一個自己的本地內(nèi)存(這是個抽象概念)筋夏,其中保存的數(shù)據(jù)是主內(nèi)存中的數(shù)據(jù)拷貝。而JMM主要是控制本地內(nèi)存和主內(nèi)存之間的數(shù)據(jù)交互的图呢。
當(dāng)圖中線程A與線程B要進(jìn)行數(shù)據(jù)交互時条篷,將要經(jīng)歷:
1)線程A把本地內(nèi)存B中更新過的共享變量刷新到主內(nèi)存中去。
2)線程B到主內(nèi)存中去讀取線程A刷新過的共享變量蛤织,然后copy一份到本地內(nèi)存B 中去赴叹。
未完待續(xù)