菜雞每日一面系列打卡15天
每天一道面試題目
助力小夥伴輕鬆拿offer
堅持就是勝利,我們一起努力!
題目描述
談談你對synchronized關鍵字的理解。
題目分析
相比之前總結的有關volatile關鍵字的考查,面試官對synchronized關鍵字的考查更是有過之而無不及,而且對synchronized與volatile關鍵字往往是結合在一起考查的。上一篇文章系統介紹了有關volatile關鍵字的作用、原理及使用場景,對volatile關鍵字尚不清楚的小夥伴,可以移步文末相關鏈接進行學習,或者點擊文章開始的每日一面專輯進行查看,而本文將系統介紹synchronized關鍵字。
題目解答
01
線程安全的定義
在談synchronized關鍵字之前,菜雞想先談一談線程安全的概念。相信絕大多數小夥伴都聽說過“線程安全”這一概念,想要了解一個概念,最好的方式就是從其定義出發。
線程安全的定義(引自《Java併發編程實戰》)如下:當多個線程同時訪問一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他的協調操作,調用這個對象的行爲都可以獲得正確的結果,那就稱這個對象是線程安全的。
根據以上描述,結合大佬們的經驗,我們可以總結出非線程安全的條件:
多線程環境。
共享資源。
沒有保證原子性、可見性和有序性。
容易對這三個條件做出說明。首先,如果是單線程的環境下,我們自然不必關係非線程安全的問題。其次,如果是沒有線程間共享的資源,也就不會出現線程安全的問題。最後,如果保證了操作的原子性、可見性和有序性,也就不會有線程安全的問題。事實上,Java內存模型就是圍繞着在併發過程中如何處理原子性、可見性和有序性這三個特徵來建立的。
02
實現線程安全的方式
有經驗的小夥伴應該瞭解,實現線程安全的方式主要有三種:
互斥同步:例如,基於synchronized關鍵字或者Lock接口實現同步。
非阻塞同步:例如,基於CAS操作的原子類,但CAS操作會出現ABA問題,如果需要解決ABA問題,可以引入版本號,也可以改用其他同步方案。
無同步方案:例如,通過ThreadLocal實現線程本地存儲。
本文關注的重點是JVM對原子性、可見性、有序性所作的支持:
關於原子性,保證原子性的方式有很多種,除了操作本身是原子性之外,還可以採用一些原子類保證原子性,可以通過加鎖的方式,或者無鎖算法保證原子性。本文主要講述的是利用synchronized關鍵字保證原子性。
關於可見性,Java主要有三個關鍵字可以保證可見性,一個是我們之前講過的volatile關鍵字,另一個是final關鍵字,還有一個就是本文提到的synchronized關鍵字。
關於有序性,Java主要有兩個關鍵字可以保證有序性,一個是我們之前講過的volatile關鍵字,另一個就是本文提到的synchronized關鍵字。
03
synchronized關鍵字
至此,本文的主角synchronized關鍵字終於登場了!synchronized關鍵字是Java中實現互斥同步最基本的手段,它是一種悲觀鎖,是一種可重入鎖(通過鎖計數器是否爲0判定對象持有或者釋放鎖的狀態)。
synchronized關鍵字的工作原理:
在同步語句塊時,synchronized關鍵字經過編譯之後,會在同步塊的前後形成monitorenter和monitorexit兩個字節碼指令,代表對象實例或者Class對象作爲線程要持有的鎖。
在修飾方法時,synchronized關鍵字經過編譯之後,會生成ACC_SYNCHRONIZED標識,代表該方法是一個同步方法。
synchronized關鍵字的不足及改進:
synchronized是Java語言的一個重量級操作,因爲線程的阻塞與喚醒會涉及到操作系統用戶態與核心態之間的轉換,會消耗大量的時間。
從JDK1.5升級到JDK1.6後,HotSpot虛擬機開發團隊實現了各種鎖優化技術,例如,適應性自旋鎖,鎖消除,鎖膨脹,輕量級鎖,偏向鎖等優化,從而實現線程間更高效的數據共享和解決競爭問題。
04
總結
synchronized關鍵字不僅僅是一個用於保證線程安全的關鍵字,它反映的是Java開發團隊乃至計算機科學界對線程安全的一些共識,如何保證線程安全是一門很深的學問,可以深究到操作系統的細節。如何高效實現線程安全是一門藝術,因此,掌握synchronized關鍵字背後的設計思想,將對於我們進行併發編程有很大的幫助。
以上便是菜雞對synchronized關鍵字的一些總結,供大家參考。
學習 | 工作 | 分享
????長按關注“有理想的菜雞”
只有你想不到,沒有你學不到