Java 面試之執行緒與鎖
程序、執行緒
程序和執行緒的狀態
- 程序:建立、就緒、執行、阻塞、終止
- 執行緒:就緒、執行、阻塞
實現多執行緒的幾種方式
- 繼承Thread類建立執行緒
- 實現Runnable介面建立執行緒
- 實現Callable介面建立新執行緒(可用Future返回結果)
各種比較
sleep()和wait(),yield()和notify()
- sleep()是Thread類的一個靜態函式,它會使呼叫執行緒睡眠(阻塞)一段時間,讓其他執行緒有機會繼續執行,但它不釋放鎖。
- wait()是Object類的方法,它會使當前執行緒阻塞,直到呼叫notify(),則被喚醒,它會釋放鎖。
- yield()是Thread類的方法,它會使執行中的執行緒重新變為就緒狀態,讓同優先順序執行緒重新競爭。
- notify()是Object類的方法,它會喚醒單個執行緒。
synchronized和volatile的區別
-
synchronized是Java中的關鍵字,是一種同步鎖。有如下幾種用法:
//1. 修飾方法(方法鎖) public synchronized void syncMethod() { //doSomething } //2. 修飾程式碼塊(物件鎖) public int synMethod(int arg){ synchronized(arg) { //doSomething } } //3. 修飾類(類鎖) public class SyncClass
鎖的級別有物件級別和類級別,1和2屬於物件級別,3屬於類級別
-
volatile 關鍵字的作用是禁止指令的重排序,強制從公共堆疊中取得變數的值,而不是從執行緒私有的資料棧中取變數的值。volatile與synchronized的區別如下:
比較點 volatile synchronized 阻塞 不會發生執行緒阻塞 會發生執行緒阻塞 修飾 變數 方法、程式碼塊、類 原子性 不能保證 可以保證 安全性 非執行緒安全 執行緒安全 解決問題 變數在多執行緒之間的可見性 多執行緒之間訪問資源的同步性
sychronized、Lock
比較點 | sychronized | Lock |
---|---|---|
解釋 | Java關鍵字 | Java介面 |
顯隱 | 隱式鎖 | 需顯示指定起始位置和終止位置 |
釋放鎖 | 獲取鎖的執行緒會在執行完同步程式碼後自動釋放鎖(或者JVM會線上程執行發生異常時釋放鎖) | 在finally中必須釋放鎖,不然容易造成執行緒死鎖 |
等待 | 一個執行緒獲得鎖後阻塞,其他執行緒會一直等待 | 執行緒不會一直等待,超時會釋放 |
鎖型別 | 可重入但不可中斷、非公平 | 可重入、可中斷、可公平也可不公平 |
ThreadLocal
設計理念是為了減少同一個執行緒內多個函式或者元件之間一些公共變數的傳遞的複雜度。作用是提供執行緒內部的區域性變數,這些變數在多執行緒環境下訪問(get/set)時能保證與其它執行緒裡的變數相對獨立。打個比方,多人(多個執行緒)使用自己的交通卡(執行緒私有變數)乘公交轉地鐵(兩個函式)。
注意:使用 ThreadLocal 時要保證能夠管理它的建立、銷燬,否則會出問題。因為 ThreadLocal 是和 Thread 繫結的,如果 Thread 是從 ThreadPool 中拿出來的,那麼意味著 Thread 可能會被複用。如果被複用,你就一定得保證這個 Thread 上一次結束的時候,其關聯的 ThreadLocal 被清空掉,否則就會串到下一次使用。
synchronized用於執行緒間的資料共享,而ThreadLocal則用於執行緒間的資料隔離。
ThreadPool的用法與優勢
執行緒池的用法
使用ThreadPoolExecutor建立執行緒池,其中5參建構函式引數列表如下:注意,在CPU密集型和I/O密集型處理時: I/O密集型佇列maxSize設定大一點,提高cpu利用率
。
-
int corePoolSize:執行緒池中核心執行緒數,一般為cpu數量
-
int maximumPoolSize: 執行緒池中執行緒總數,一般為2*cpu數量
-
long keepAliveTime:執行緒池中非核心執行緒閒置超時時長
-
TimeUnit unit:keepAliveTime的單位
-
BlockingQueue workQueue:執行緒池中的任務佇列,維護著等待執行的Runnable物件
- SynchronousQueue:接收到任務時,會直接提交給執行緒處理,而不保留它
- LinkedBlockingQueue:接收到任務時,如果當前執行緒數小於核心執行緒數,則新建核心執行緒處理任務;如果當前執行緒數等於核心執行緒數,則進入佇列等待
- ArrayBlockingQueue:接收到任務時,如果沒有達到corePoolSize的值,則新建核心執行緒執行任務;如果達到了,則入隊等候;如果佇列已滿,則新建非核心執行緒執行任務;如果匯流排程數到了maximumPool,且佇列也滿了,則發生錯誤
- DelayQueue:接收到任務時,首先先入隊,只有達到了指定的延時時間,才會執行任務。注意:佇列內元素必須實現Delayed介面,這就意味著你傳進去的任務必須先實現Delayed介面
-
RejectedExecutionHandler handler:BlockingQueue 打滿時的幾種拒絕策略
- Abort策略:預設策略,新任務提交時直接丟擲未檢查的異常。
- RejectedExecutionException,該異常可由呼叫者捕獲。
- CallerRuns策略:為調節機制,既不拋棄任務也不丟擲異常,而是將某些任務回退到呼叫者。不會線上程池的執行緒中執行新的任務,而是在呼叫exector的執行緒中執行新的任務。
- Discard策略:新提交的任務被拋棄。
- DiscardOldest策略:佇列的是“隊頭”的任務,然後嘗試提交新的任務。
向執行緒池提交一個要執行的任務
threadPoolExecutor.execute(runnable);
使用執行緒池的優勢:
- 降低資源消耗:重複利用已建立的執行緒,降低建立和銷燬造成的消耗。
- 提高響應速度:任務可以不需要等到執行緒建立就能立即執行(參考上條)。
- 提高管理性:可以進行統一的分配、調優和監控。
concurrent包
- Executor介面:具體Runnable任務的執行者。
- Executors類:建立執行緒池工具類(阿里手冊禁止用此工具類)。
- ExecutorService介面:執行緒池管理者,可提交Runnable、Callable讓其排程。
- ThreadPoolExecutor類:執行緒池工具類。(ExecutorService的一種具體實現)。
- CompletionService介面:ExecutorService的擴充套件,可以獲得執行緒執行結果。
- ReentrantLock類:可重入互斥鎖(實現Lock介面)。
- BlockingQueue介面:阻塞佇列。
- Future介面:一個執行緒執行結束後取返回的結果,還提供了cancel()終止執行緒。
- CountDownLatch類:當計數器值到達0時,它表示所有的執行緒已經完成了任務,然後在閉鎖上等待的執行緒就可以恢復執行任務。
鎖
死鎖的必要條件,怎麼處理死鎖
死鎖產生的必要條件
- 互斥條件:程序對所分配的資源進行排他性控制,即在一段時間內某資源僅為一個程序所佔有。此時若有其他程序請求該資源,則請求程序只能等待。
- 不剝奪條件:程序所獲得的資源在未使用完畢之前,不能被其他程序強行奪走,即只能 由獲得該資源的程序主動釋放。
- 請求和保持條件:程序已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其他程序佔有,此時請求程序被阻塞,但對自己已獲得的資源保持不放。
- 環路等待條件:存在一個程序——資源的環形鏈,即程序集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。
避免死鎖:
- 加鎖順序(執行緒按一定順序加鎖,若所有執行緒都按相同順序獲得鎖,就能避免死鎖)
- 加鎖時限(執行緒獲取鎖時加上時限,超時則放棄並釋放所佔有的鎖,就能避免死鎖)
- 死鎖檢測(一個更優的預防機制,主要針對不可能實現按序加鎖和加鎖時限的場景)
鎖的型別
鎖 | 解釋 |
---|---|
公平鎖/非公平鎖 | 公平鎖是指多個執行緒按照申請鎖的順序來獲取鎖 |
可重入鎖 | 同一執行緒在外層方法已獲取鎖時,進入內層方法會自動獲取鎖 |
獨享鎖/共享鎖 | 一次可被單個/多個執行緒所持有,例如ReadWriteLock的寫鎖/讀鎖 |
互斥鎖/讀寫鎖 | 一種互斥鎖:ReentrantLock;一種讀寫鎖:ReadWriteLock |
樂觀鎖/悲觀鎖 | 悲觀鎖認為對於同一個資料的併發操作一定會發生修改 |
分段鎖 | 一種鎖的設計,具體應用有ConcurrentHashMap |
自旋鎖 | 嘗試獲取鎖的執行緒不會立即阻塞,而是採用迴圈的方式去嘗試獲取 |
偏向鎖/輕量級鎖/重量級鎖 | 指鎖的狀態,並且是針對synchronized |
偏向鎖是指一段同步程式碼一直被一個執行緒所訪問,那麼該執行緒會自動獲取鎖。輕量級鎖是指當鎖是偏向鎖的時候,被另一個執行緒所訪問,偏向鎖就會升級為輕量級鎖,其他執行緒會通過自旋的形式嘗試獲取鎖,不會阻塞,提高效能。重量級鎖是指當鎖為輕量級鎖的時候,另一個執行緒雖然是自旋,但當自旋一定次數還沒獲取到鎖的時候,就會進入阻塞,該鎖膨脹為重量級鎖。
分散式鎖
一種跨伺服器(JVM)控制共享資源訪問的互斥機制。在分散式系統環境下,一個方法在同一時間只能被一臺機器的一個執行緒執行。
- 基於資料庫實現分散式鎖:在資料庫中建立一張表,表中包含方法名欄位,並在方法名欄位上建立唯一索引,想要執行某個方法,就使用這個方法名向表中插入資料,成功插入則獲取鎖,執行完成後刪除對應的行資料釋放鎖。
- 基於快取(Redis等)實現分散式鎖:獲取鎖的時候,使用setnx加鎖,並使用expire命令為鎖新增一個超時時間,超過該時間則自動釋放鎖,鎖的value值為當前時間加上鎖定時間,釋放鎖的時候執行delete進行釋放。
- 基於Zookeeper實現分散式鎖:①建立一個目錄dislock;②執行緒A想獲取鎖就在dislock目錄下建立臨時順序節點;③獲取dislock目錄下所有的子節點,然後獲取比自己小的兄弟節點,如果不存在,則說明當前執行緒順序號最小,獲得鎖;④執行緒B獲取所有節點,判斷自己不是最小節點,設定監聽比自己次小的節點;⑤執行緒A處理完,刪除自己的節點,執行緒B監聽到變更事件,判斷自己是不是最小的節點,如果是則獲得鎖。
相關推薦
Java 面試之執行緒與鎖
程序、執行緒 程序和執行緒的狀態 程序:建立、就緒、執行、阻塞、終止 執行緒:就緒、執行、阻塞 實現多執行緒的幾種方式 繼承Thread類建立執行緒 實現Runnable介面建立執行緒 實現Callable介面建立新執行緒(可用Future返回結果)
Java開發之執行緒同步造成的執行緒死鎖
案例解析: 兩個人面對面過獨木橋,甲和乙都已經在橋上走了一段距離,即佔用了橋的資源,甲如果想通過獨木橋的話,乙必須退出橋面讓出橋的資源,讓甲通過,但是乙不服,為什麼讓我先退出去,我還想先過去呢,於是就僵持不下,導致誰也過不了橋,這就是死鎖。 死鎖產生情況解析: 1.互斥條件(
Java筆記-多執行緒之執行緒死鎖問題加簡單舉例
死鎖 導致死鎖的原因 Java中死鎖最簡單的情況是,一個執行緒T1持有鎖L1並且申請獲得鎖L2,而另一個執行緒T2持有鎖L2並且申請獲得鎖L1,因為預設的鎖申請操作都是阻塞的,所以執行緒T1和T2永遠被阻塞了。導致了死鎖。 這是最容易理解也是最簡單的死
Java多執行緒與鎖模型-順序鎖與資源鎖
順序鎖:當應用程式使用2把以上的鎖時,就容易出現因為多執行緒獲取鎖的順序不同而死鎖的情形,包括交叉獲取應用程式範圍內的多把已知鎖、交叉獲取應用程式與第三方方法中的多把鎖而造成的順序死鎖。絕大多數死鎖都是因為CPU排程多執行緒時,在執行時序上是交叉進行的而造成亂序獲得多把鎖,從
JAVA語言規範-執行緒和鎖章節之同步、等待和通知
原文連結 本文是Oracle官方《Java語言規範》的譯文 JAVA語言規範:執行緒和鎖 1 同步 JAVA程式語言提供了執行緒間通訊的多種機制。這些方法中最基本的是同步化,此方法是使用監視器實現的。JAVA中每個物件與一個監視器相關聯,一個執行緒可以加鎖和解鎖監視器。一次僅有一個執行緒可能
Java執行緒與鎖
Java執行緒與鎖 本篇是 《深入理解Java虛擬機器》的最後一章, 在此涉及到了執行緒安全, 但並不是如何從程式碼層次來實現執行緒安全, 而是虛擬機器本身對執行緒安全做出了哪些努力, 在安全與效能之間又採取了哪些優化措施. 那麼一步步來梳理這些概念. 三種執行緒概念——核心執行緒、輕量級程序、使用者執
java 併發(執行緒&鎖)
java 併發(執行緒&鎖) ##執行緒 ###執行緒概念 作業系統排程的最小單元是執行緒,也叫輕量級程序(LightWeight Process),在一個程序裡可以建立多個執行緒,這些執行緒都擁有各自的計數器、堆疊和區域性變數等屬性,並且能夠訪問共享的記憶體變數。處
C++:多執行緒與鎖
多執行緒是小型軟體開發必然的趨勢。C++11將多執行緒相關操作全部整合到標準庫中了,省去了某些坑庫的編譯,真是大大的方便了軟體開發。多執行緒這個庫簡單方便實用,下面給出簡單的例子 #include <iostream> #inc
Java基礎之執行緒池
一、執行緒池概念 執行緒池:其實就是一個容納多個執行緒的容器,其中的執行緒可以反覆使用,省去了頻繁建立執行緒物件的操作,無需反覆建立執行緒而消耗過多資源。 二、工作原理 三、合理利用執行緒池的好處 降低資源消耗。減少了建立和銷燬執行緒的次數,每個工作執行緒都可以
Java面試準備-執行緒
問題連結轉載 Java面試通關要點彙總集【終極版】 一、建立執行緒的方式及實現 方式有三種:繼承Thread類建立執行緒類,通過Runnable介面建立執行緒類和通過Callable和Future建立執行緒 1)繼承Thread類建立執行緒類 定義Thre
java併發之----執行緒的建立方法
一、執行緒的建立 (1)繼承Thread類 需要實現run方法,通過呼叫start方法啟動執行緒 public class MyThread extends Thread{ @Override public void run(){//實現run方法 //以下根據自己的需要
Java面試------多執行緒篇
多執行緒篇 一.什麼是執行緒? 執行緒是作業系統能夠進行運算排程的最小單位,它被包含在程序之中,是程序中的實際運作單位。程式設計師可以通過它進行多處理器程式設計,你可以使用多執行緒對運算密集型任務提速。比如,如果一個執行緒完成一個任務要 100 毫秒,那麼用十個執行緒完成
Java基礎之執行緒
程序與執行緒 程序:程序是指記憶體中執行得一個程式,每個程序都有一個獨立的記憶體空間,一個應用程式可以同時執行多個程序;程序是程式執行的一次過程,是系統執行程式的基本單位。 執行緒:程序內部以個單獨的獨立執行單元,一個程序可以包含多個執行緒。 程序與執行緒的區別: 程序:有獨立的儲存空間,程序
java在子執行緒與主執行緒傳遞資料(回撥函式)
預習知識點: 什麼是回撥函式? 下面是知乎大神的回答,簡直不能再精闢 程式碼: package kun.thread; public class THread { static C c=new C(); //flag用來標誌子執行緒執行結束 stati
Java面試--關於執行緒
我寫在這裡面的東西,都是自己一些平時的積累,來自於集合各路大佬的心得和自己的一點小總結,只為自己學習圖個方便,也是為了強迫自己有個記錄的習慣,希望可以一直堅持寫! 1.什麼是執行緒? 我們電腦中執行的程式可以看作是一個程序,就像是一個大的管道,裡面包含了很多個小的管道,每
一道JAVA面試,執行緒安全和靜態內部類
前言:4月1號去一家網際網路公司面試,做了一份筆試。考察的內容也非常基礎,但是裡面卻充滿著各種各樣的擴充套件。但是這份題我做得並不好,平時用框架什麼的用多了,反而基礎顯得非常不紮實。憑著記憶寫起最後一套題目。記一下,紮實一下自己的基礎。 程式碼 /*
(轉)Java中的守護執行緒 Java的守護執行緒與非守護執行緒
Java的守護執行緒與非守護執行緒 守護執行緒與非守護執行緒 最近在看多執行緒的Timer章節,發現運用到了守護執行緒,感覺Java的基礎知識還是需要補充。 Java分為兩種執行緒:使用者執行緒和守護執行緒 所謂守護執行緒是指在程式執行的時候在後臺提供一
Java面試——多執行緒
1、什麼是執行緒? 執行緒是指程式在執行的過程中,能夠執行程式程式碼的一個執行單元。Java語言中,執行緒有四種狀態:執行、就緒、掛起、結束 2、執行緒與程序的區別? 程序是指一段正在執行的程式。而執行緒有事也被稱為輕量級程序,它是程式執行的最小單元,一個程
VisualVM(9) 排查JAVA應用程式執行緒死鎖
Java虛擬機器效能管理神器 - VisualVM(9) 排查JAVA應用程式執行緒鎖 1. JAVA應用程式執行緒鎖原因 這個例子比較極端,一般情況下,出現鎖競爭激烈是比較常見的。 2. 排查JA
java面試-多執行緒
面試總結-多執行緒1 執行緒狀態先簡單的看一下執行緒之間的狀態扭轉圖從上面的圖中可以看出執行緒大概有就緒,執行,阻塞,死亡這幾個狀態,各個狀態之間的扭轉在圖中描寫挺清晰2 執行緒池面試過程中,經常會被問到執行緒池執行緒池的一些核心引數:coreSize, maxSize,ov