記一次Java面試問題點總結

来源:https://www.cnblogs.com/zhmlearn/archive/2020/01/20/12201071.html

引言 昨日接了一個阿裡外包的電話面試,問了一些技術問題感覺到自己是真的菜,接觸Java開發已經也有一段時間,技術方面說來慚愧,一直以來只是局限於框架工具的用法,也沒有進行瞭解其實現的原理,更重要的是一直沒有歸納和總結,這次把這些問題記錄下來,相關的知識點也找了一些資料學習下。 問題 1. Count ...


引言

  昨日接了一個阿裡外包的電話面試,問了一些技術問題感覺到自己是真的菜,接觸Java開發已經也有一段時間,技術方面說來慚愧,一直以來只是局限於框架工具的用法,也沒有進行瞭解其實現的原理,更重要的是一直沒有歸納和總結,這次把這些問題記錄下來,相關的知識點也找了一些資料學習下。

問題

1. CountDownLanch的工作原理

實現原理:計數器的值由構造函數傳入,並用它初始化AQS的state值。當線程調用await方法時會檢查state的值是否為0,如果是就直接返回(即不會阻塞);如果不是,將表示該節點的線程入列,然後將自身阻塞。當其它線程調用countDown方法會將計數器減1,然後判斷計數器的值是否為0,當它為0時,會喚醒隊列中的第一個節點,由於CountDownLatch使用了AQS的共用模式,所以第一個節點被喚醒後又會喚醒第二個節點,以此類推,使得所有因await方法阻塞的線程都能被喚醒而繼續執行。

  引用別人的博客里的一段話,詳細請點擊:Java併發包中CountDownLatch的工作原理、使用示例

題外話: 

什麼是 AQS(抽象的隊列同步器) 
AbstractQueuedSynchronizer類如其名,抽象的隊列式的同步器,AQS定義了一套多線程訪問 共用資源的同步器框架,許多同步類實現都依賴於它,如常用的 ReentrantLock/Semaphore/CountDownLatch。 

 

 

它維護了一個 volatile int state(代表共用資源)和一個 FIFO 線程等待隊列(多線程爭用資源被 阻塞時會進入此隊列)。這裡 volatile 是核心關鍵詞,具體 volatile 的語義,在此不述。state 的 訪問方式有三種: getState() setState()  compareAndSetState()

 

AQS只是一個框架,具體資源的獲取/釋放方式交由自定義同步器去實現,AQS這裡只定義了一個 介面,具體資源的獲取交由自定義同步器去實現了(通過state的get/set/CAS)之所以沒有定義成 abstract,是因為獨占模式下只用實現 tryAcquire-tryRelease,而共用模式下只用實現 tryAcquireShared-tryReleaseShared。如果都定義成abstract,那麼每個模式也要去實現另一模 式下的介面。不同的自定義同步器爭用共用資源的方式也不同。自定義同步器在實現時只需要實 現共用資源 state 的獲取與釋放方式即可,至於具體線程等待隊列的維護(如獲取資源失敗入隊/ 喚醒出隊等),AQS已經在頂層實現好了。自定義同步器實現時主要實現以下幾種方法:
1. isHeldExclusively():該線程是否正在獨占資源。只有用到condition才需要去實現它。

2. tryAcquire(int):獨占方式。嘗試獲取資源,成功則返回true,失敗則返回false。

3. tryRelease(int):獨占方式。嘗試釋放資源,成功則返回true,失敗則返回false。

4. tryAcquireShared(int):共用方式。嘗試獲取資源。負數表示失敗;0 表示成功,但沒有剩餘 可用資源;正數表示成功,且有剩餘資源。

5. tryReleaseShared(int):共用方式。嘗試釋放資源,如果釋放後允許喚醒後續等待結點返回 true,否則返回false。 

AQS定義兩種資源共用方式

1.Exclusive獨占資源 -ReentrantLock Exclusive(獨占,只有一個線程能執行,如ReentrantLock)

2.Share共用資源 -Semaphore/CountDownLatch Share(共用,多個線程可同時執行,如Semaphore/CountDownLatch)。 

3.實現獨占和共用兩種 方式,一般來說,自定義同步器要麼是獨占方法,要麼是共用方式,他們也只需實現 tryAcquiretryRelease、tryAcquireShared-tryReleaseShared 中的一種即可。但 AQS 也支持自定義同步器 同時實現獨占和共用兩種方式,如ReentrantReadWriteLock

 

同步器的實現是ABS核心,以ReentrantLock為例,state初始化為0,表示未鎖定狀態。A線程 lock()時,會調用 tryAcquire()獨占該鎖並將 state+1。此後,其他線程再 tryAcquire()時就會失 敗,直到A線程unlock()到state=0(即釋放鎖)為止,其它線程才有機會獲取該鎖。當然,釋放 鎖之前,A 線程自己是可以重覆獲取此鎖的(state 會累加),這就是可重入的概念。但要註意, 獲取多少次就要釋放多麼次,這樣才能保證state是能回到零態的。 

以CountDownLatch以例,任務分為N個子線程去執行,state也初始化為N(註意N要與 線程個數一致)。這 N 個子線程是並行執行的,每個子線程執行完後 countDown()一次,state 會CAS減1。等到所有子線程都執行完後(即state=0),會unpark()主調用線程,然後主調用線程 就會從await()函數返回,繼續後餘動作。

2. 說一下自旋鎖的原理

如果持有鎖的線程能在很短的時間內釋放鎖資源,那麼那些等待競爭鎖的線程就不需要做內核態和用戶態之間的切換,它們只需要等一等(自旋),等待鎖釋放之後即可立即獲取鎖,這樣避免了用戶線程和內核切換的消耗

線程自旋是需要消耗 cup 的,說白了就是讓 cup 在做無用功,如果一直獲取不到鎖,那線程 也不能一直占用cup自旋做無用功,所以需要設定一個自旋等待的最大時間。
如果持有鎖的線程執行的時間超過自旋等待的最大時間扔沒有釋放鎖,就會導致其它爭用鎖 的線程在最大等待時間內還是獲取不到鎖,這時爭用線程會停止自旋進入阻塞狀態。

自旋鎖的優缺點
自旋鎖儘可能的減少線程的阻塞,這對於鎖的競爭不激烈,且占用鎖時間非常短的代碼塊來 說性能能大幅度的提升,因為自旋的消耗會小於線程阻塞掛起再喚醒的操作的消耗,這些操作會 導致線程發生兩次上下文切換!
但是如果鎖的競爭激烈,或者持有鎖的線程需要長時間占用鎖執行同步塊,這時候就不適合 使用自旋鎖了,因為自旋鎖在獲取鎖前一直都是占用 cpu 做無用功,占著 XX 不 XX,同時有大量 線程在競爭一個鎖,會導致獲取鎖的時間很長,線程自旋的消耗大於線程阻塞掛起操作的消耗, 其它需要cup的線程又不能獲取到cpu,造成cpu的浪費。所以這種情況下我們要關閉自旋鎖;

3. synchronized中類鎖和對象鎖的區別

 這麼基礎的一個問題我竟然遺忘了。。因為之前記的是鎖static方法和非static方法的區別,其實鎖static方法就相當於鎖類class,因為static方法是所有的類共用的,類鎖是鎖當前類的所有實例,對象鎖是鎖當前實例對象,詳細的引入別人的博客:Java鎖Synchronized對象鎖和類鎖的區別

4. volatile關鍵字的作用

     問:說下項目中用到的設計模式

     答:單例模式、責任鏈模式、工程模式、模板模式等等

     問:那你說下你們平常單例模式怎麼實現的

     答:我們用的是雙向檢查

     問:你們雙向檢查有用到volatile吧,那你說下這個關鍵字有什麼作用

     答:它可以用來保證線程每次獲取的對象都是最新狀態

     問:除此之外還有什麼作用

     答: 卒

  真的是

您的分享是我們最大的動力!

更多相關文章
  • 上面這張表格實際上是一個n行 6列的二維數組。 多維數組的語法: 註意:每一個中括弧表示一個維度。 習題 設計一個程式,可以保存n年的各科成績,可以對這些年的成績進行查詢。 測試用例:保存3年的成績,查詢第三年的生物成績。 ...
  • 很多人都礙於Python培訓班的高昂費用和有限的空餘時間都選擇自學Python,但是沒有老師幫助,顯得有些迷茫,不知應該從何處學起,也不知識看書學習還是應該看視頻學習。本就來談談這個話題。 ...
  • 一、安裝Docker 1、我是虛擬機裝的Centos7,linux 3.10 內核,docker官方說至少3.8以上,建議3.10以上(ubuntu下要linux內核3.8以上) root賬戶登錄,查看內核版本如下 uname -a 2、把yum包更新到最新 yum update (期間要選擇確認, ...
  • 一.常量 聲明常量可以方便代碼的修改,提高復用性. const int maxn=10000; const int N=10000+10; const double exp=1e-6; 同時,聲明常量也可以減少重覆運算,提高代碼速度,例子如下: string s; cin>>s; for(int i ...
  • 發現一個驗證字元串是否包含中文滴時候,一個比正則更好使滴方法,而且是golang 自帶滴驗證。 不需要自己寫正則驗證,代碼如下: package main import ( "fmt" "regexp" "unicode" ) func main() { s1 := "我是中國人hello word ...
  • 基本構架 所有的C程式都有一個 main 函數.其後包含在大括弧中的是 main 函數的內容. main函數是程式的入口,程式運行後,先進入 main 函數,然後一次執行 main 函數體中的語句. 這是一個例子: 簡單來說,寫在 main 中的內容會在程式啟動時執行.main 函數中的內容是程式的 ...
  • 功能描述:做的是物聯網的項目,Excel導入實現的功能是將Excel中的數據批量的導入AEP系統,再導入我們系統中。目前已經完成該功能,前端還會添加進度條優化。Excel模板: 前端向後端傳遞的參數: 前端代碼: <Upload name="wlwDeviceFile" ref="upload" : ...
  • 導入下列依賴包,搞定 sudo apt-get install python3 python-dev python3-dev build-essential libssl-dev libffi-dev libxml2-dev libxslt1-dev zlib1g-dev python-pip 上訴 ...
一周排行
  • 前幾天發佈了 "抄抄《CSS 故障藝術》的動畫" 這篇文章,在這篇文章里介紹瞭如何使用Win2D繪製文字然後配合BlendEffect製作故障藝術的動畫。本來打算就這樣收手不玩這個動畫了,但後來又發現性能不符合理想。明明只是做做Resize動畫和用BlendEffect混合,為什麼性能會這麼差呢? ...
  • 控制條控制項: progressBar 不能按照你程式的進程自動變化,需認為計算,調整變化量 private void progressBar1_Click(object sender, EventArgs e) { this.progressBar1.Maximum = 100;//設置進度條最大長 ...
  • 首先創建一個asp.net core web應用程式 第二步 目前官方預置了7種模板項目供我們選擇。從中我們可以看出,既有我們熟悉的MVC、WebAPI,又新添加了Razor Page,以及結合比較流行的Angular、React前端框架的模板項目。 空項目模板 Program.cs using S ...
  • 對閉包的理解 1.對於成員變數和局部變數:成員變數就是方法外部,類的內部定義的變數;局部變數就是方法或語句塊內部定義的變數。局部變數必須初始化。 形式參數是局部變數,局部變數的數據存在於棧記憶體中。棧記憶體中的局部變數隨著方法的消失而消失。成員變數存儲在堆中的對象裡面,由垃圾回收器負責回收。 成員變數它 ...
  • Xamarin.Forms讀取並展示Android和iOS通訊錄 TerminalMACS客戶端 本文同步更新地址: https://dotnet9.com/11520.html https://terminalmacs.com/861.html 閱讀導航: 一、功能說明 二、代碼實現 三、源碼獲取 ...
  • 做下對文件複製操作相關的筆記: /// <summary> /// 文件幫助類 /// </summary> public class FileHelper { /// <summary> /// 複製一個目錄下所有文件到一個新目錄下 /// </summary> /// <param name=" ...
  • 前言 有一個東西叫做鴨子類型,所謂鴨子類型就是,只要一個東西表現得像鴨子那麼就能推出這玩意就是鴨子。 C 裡面其實也暗藏了很多類似鴨子類型的東西,但是很多開發者並不知道,因此也就沒法好好利用這些東西,那麼今天我細數一下這些藏在編譯器中的細節。 不是只有 和 才能 在 C 中編寫非同步代碼的時候,我們經 ...
  • [toc] 1.應用背景 底端設備有大量網路報文(位元組數組):心跳報文,數據採集報文,告警報文上報。需要有對應的報文結構去解析這些位元組流數據。 2.結構體解析 由此,我第一點就想到了用結構體去解析。原因有以下兩點: 2.1.結構體存在棧中 類屬於引用類型,存在堆中;結構體屬於值類型,存在棧中,在一個 ...
  • 《深入淺出 C#》 (第3版) [作者] (美) Andrew Stellman (美) Jennifer Greene[譯者] (中) 徐陽 丁小峰 等譯[出版] 中國電力出版社[版次] 2016年08月 第1版[印次] 2018年04月 第4次 印刷[定價] 148.00元 【引子】 要學習編程 ...
  • 記錄使用對象初始值設定項初始化對象。 using System; using System.Collections.Generic; namespace ConsoleApp2 { class Program { static void Main(string[] args) { // 使用構造函數 ...
x