Java并發(fā)編程基礎(chǔ)面試題


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ù)交互的图呢。


image.png

當(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ù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瞳筏,隨后出現(xiàn)的幾起案子稚瘾,更是在濱河造成了極大的恐慌,老刑警劉巖姚炕,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摊欠,死亡現(xiàn)場離奇詭異丢烘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)些椒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門播瞳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人免糕,你說我怎么就攤上這事赢乓。” “怎么了石窑?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵牌芋,是天一觀的道長。 經(jīng)常有香客問我松逊,道長躺屁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任经宏,我火速辦了婚禮犀暑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烁兰。我一直安慰自己耐亏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布沪斟。 她就那樣靜靜地躺著广辰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪币喧。 梳的紋絲不亂的頭發(fā)上轨域,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音杀餐,去河邊找鬼干发。 笑死,一個胖子當(dāng)著我的面吹牛史翘,可吹牛的內(nèi)容都是我干的枉长。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼琼讽,長吁一口氣:“原來是場噩夢啊……” “哼必峰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钻蹬,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤吼蚁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肝匆,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粒蜈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了旗国。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枯怖。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖能曾,靈堂內(nèi)的尸體忽然破棺而出度硝,到底是詐尸還是另有隱情,我是刑警寧澤寿冕,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布蕊程,位于F島的核電站,受9級特大地震影響蚂斤,放射性物質(zhì)發(fā)生泄漏存捺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一曙蒸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧岗钩,春花似錦纽窟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至视搏,卻和暖如春审孽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浑娜。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工佑力, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筋遭。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓打颤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親漓滔。 傳聞我的和親對象是個殘疾皇子编饺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容