【面試篇】多線程

基礎概念

線程的生命周期有哪些狀態?它們是如何轉換的?
答案:線程的生命周期有以下六種狀態:
新建(New):線程被創建但尚未啟動,此時線程對象已被分配內存空間,相關屬性已被初始化。
就緒(Runnable):線程調用start()方法后進入此狀態,表明線程已準備好運行,等待系統調度獲取 CPU 資源。
運行(Running):線程獲取到 CPU 資源,正在執行run()方法中的代碼邏輯。
阻塞(Blocked):線程因某些原因暫停執行,放棄 CPU 使用權。如等待獲取鎖、執行wait()方法進入等待狀態、執行 I/O 操作等。
超時等待(Timed Waiting):線程進入等待狀態,但有指定的等待時間。例如通過Thread.sleep(long millis)、wait(long timeout)等方法進入此狀態,時間到期后會自動返回就緒狀態。
終止(Terminated):線程執行完run()方法中的代碼,或者因異常等原因提前結束,線程進入終止狀態,此時線程的生命周期結束。
狀態轉換:新建狀態的線程調用start()方法進入就緒狀態;就緒狀態的線程被 CPU 調度選中進入運行狀態;運行狀態的線程執行wait()方法或等待獲取鎖等情況會進入阻塞狀態,執行Thread.sleep(long millis)等方法會進入超時等待狀態;阻塞狀態和超時等待狀態的線程在滿足相應條件(如被notify()喚醒、獲取到鎖、等待時間結束等)后會回到就緒狀態;運行狀態的線程執行完run()方法或出現異常等會進入終止狀態。

同步與鎖

synchronized關鍵字的作用是什么?它是如何實現同步的?
答案:synchronized關鍵字用于實現線程之間的同步,確保在同一時刻只有一個線程能夠訪問被 synchronized修飾的代碼塊或方法,從而保證數據的一致性和完整性。
當一個線程訪問被synchronized修飾的代碼塊或方法時,它會先獲取對象的鎖(如果是靜態方法,則獲取類的鎖)。如果鎖已經被其他線程持有,那么當前線程會進入阻塞狀態,直到獲取到鎖。在獲取到鎖后,線程才能執行相應的代碼。當線程執行完同步代碼塊或方法后,會釋放鎖,以便其他線程可以獲取鎖并執行同步代碼。
說說ReentrantLock和synchronized的區別。
答案:
實現機制:synchronized是 Java 語言的關鍵字,由 JVM 底層實現;ReentrantLock是 Java.util.concurrent 包中的類,通過代碼實現。
鎖的獲取與釋放:synchronized在代碼塊或方法執行完后自動釋放鎖;ReentrantLock需要手動調用unlock()方法釋放鎖,通常在finally塊中進行,以確保鎖一定會被釋放,否則可能導致死鎖。
可重入性:兩者都具有可重入性,即同一個線程可以多次獲取同一個鎖。
公平性:synchronized是非公平鎖,線程獲取鎖的順序不確定;ReentrantLock可以通過構造函數參數指定是否為公平鎖,公平鎖會按照線程請求鎖的順序分配鎖,減少線程饑餓現象,但會降低一定的性能。
功能特性:ReentrantLock提供了更多的功能特性,如可以嘗試獲取鎖(tryLock()方法)、可中斷地獲取鎖(lockInterruptibly()方法)等,而synchronized不具備這些功能。
什么是死鎖?如何避免死鎖?
答案:死鎖是指兩個或多個線程在執行過程中,因爭奪資源而造成的一種互相等待的狀態,若無外力作用,這些線程將永遠無法繼續執行。例如,線程 A 持有資源 1 并等待資源 2,而線程 B 持有資源 2 并等待資源 1,此時兩個線程相互等待,形成死鎖。
避免死鎖的方法有:
按順序獲取鎖:確保所有線程按照相同的順序獲取鎖,避免鎖的交叉獲取。
避免鎖嵌套:盡量減少在一個同步塊中獲取另一個鎖的情況,降低死鎖發生的可能性。
設置鎖超時:使用具有超時機制的鎖獲取方法,如ReentrantLock的tryLock(long timeout, TimeUnit unit)方法,當獲取鎖超時后,線程可以放棄等待,避免無限期等待。
使用資源分配圖檢測:在程序運行過程中,通過資源分配圖來檢測是否存在死鎖,如果發現死鎖,可以采取相應的措施進行處理,如終止某些線程或釋放某些資源。
線程間通信

線程間如何進行通信?請列舉幾種方式。

答案:
使用wait()、notify()和notifyAll()方法:這三個方法是Object類的方法,在同步代碼塊或同步方法中使用。一個線程調用wait()方法后會釋放鎖并進入等待狀態,其他線程可以調用notify()或notifyAll()方法喚醒等待的線程。notify()方法隨機喚醒一個等待的線程,notifyAll()方法喚醒所有等待的線程。
使用BlockingQueue:BlockingQueue是一個阻塞隊列,當隊列滿時,向隊列中添加元素的線程會被阻塞;當隊列為空時,從隊列中獲取元素的線程會被阻塞。通過這種方式實現線程間的通信和同步,例如ArrayBlockingQueue、LinkedBlockingQueue等。
使用CountDownLatch:CountDownLatch可以讓一個或多個線程等待其他線程完成一組操作后再繼續執行。通過countDown()方法減少計數器的值,當計數器的值為 0 時,等待的線程被喚醒繼續執行。
使用CyclicBarrier:CyclicBarrier用于讓一組線程互相等待,直到所有線程都到達某個屏障點,然后再一起繼續執行。它可以重復使用,當所有線程都到達屏障后,屏障會被重置,可以再次使用。
解釋一下wait()和sleep()方法的區別。
答案:
所屬類:wait()方法是Object類的方法,而sleep()方法是Thread類的方法。
釋放鎖的行為:wait()方法會釋放當前線程持有的對象鎖,使得其他線程可以獲取該鎖并訪問同步代碼塊或方法;sleep()方法不會釋放鎖,線程在睡眠期間仍然持有鎖,其他線程無法訪問被該線程鎖住的資源。
使用場景:wait()方法通常用于線程間的通信和協作,例如一個線程等待另一個線程完成某個操作后再繼續執行;sleep()方法主要用于讓線程暫停一段時間,例如在循環中控制執行頻率,或者在某些操作之間添加延遲。
喚醒方式:wait()方法需要被其他線程調用notify()或notifyAll()方法喚醒,或者等待指定的時間后自動喚醒;sleep()方法在指定的睡眠時間到達后自動喚醒。

并發容器與框架

請介紹一下ConcurrentHashMap的實現原理。
答案:ConcurrentHashMap是 Java 中用于在多線程環境下高效存儲和訪問數據的哈希表實現。它采用了分段鎖(Segment)的技術,將整個哈希表分成多個段,每個段都有自己的鎖。在 JDK 8 及以后的版本中,ConcurrentHashMap摒棄了分段鎖的概念,采用了 CAS(Compare and Swap)操作和synchronized關鍵字來實現并發安全。
當進行插入、刪除或查詢操作時,ConcurrentHashMap首先根據鍵的哈希值確定要操作的桶(bucket)。對于插入操作,會使用 CAS 操作嘗試將新元素插入到桶中,如果桶為空,則直接插入;如果桶不為空,則可能需要對桶中的元素進行遍歷和更新。在遍歷和更新過程中,會使用synchronized關鍵字對桶進行加鎖,以確保同一時刻只有一個線程能夠訪問該桶。對于查詢操作,由于ConcurrentHashMap的桶中的元素是通過鏈表或紅黑樹來存儲的,所以查詢操作可以在不加鎖的情況下進行,通過 volatile 關鍵字保證了桶中元素的可見性,從而實現了高并發下的高效查詢。
Java 中的BlockingQueue有哪些實現類?它們的特點是什么?
答案:
ArrayBlockingQueue:基于數組實現的有界阻塞隊列,在創建時需要指定隊列的容量。它按照先進先出(FIFO)的原則對元素進行排序,插入和刪除操作在隊列的兩端進行,使用一把鎖來保證并發安全。
LinkedBlockingQueue:基于鏈表實現的阻塞隊列,可以指定隊列的容量,也可以不指定,默認容量為Integer.MAX_VALUE。它同樣按照 FIFO 原則對元素進行排序,插入和刪除操作分別在鏈表的頭和尾進行,使用兩把鎖(一把用于讀操作,一把用于寫操作)來提高并發性能。
PriorityBlockingQueue:基于優先級堆實現的無界阻塞隊列,元素按照優先級進行排序。在插入元素時,會根據元素的優先級將其插入到合適的位置,取出元素時,會取出優先級最高的元素。它使用一把鎖來保證并發安全。
DelayQueue:基于優先級隊列實現的無界阻塞隊列,隊列中的元素必須實現Delayed接口,該接口定義了getDelay(TimeUnit unit)方法用于獲取元素的延遲時間。只有當元素的延遲時間到期后,才能從隊列中取出元素。它使用一把鎖和一個條件變量來實現延遲隊列的功能。
談談你對Executor框架的理解。它有哪些主要的組件?
答案:Executor框架是 Java 中用于管理和執行線程任務的框架,它提供了一種高效的方式來創建、執行和管理線程,將任務的提交與執行解耦,使得代碼更加易于維護和擴展。
主要組件包括:
Executor接口:定義了一個execute(Runnable command)方法,用于執行給定的Runnable任務。
ExecutorService接口:繼承自Executor接口,提供了更豐富的方法,如提交任務、關閉線程池等。它可以管理一組線程,并且可以通過不同的策略來分配任務給線程執行。
ThreadPoolExecutor類:ExecutorService接口的主要實現類,用于創建線程池。它可以根據不同的參數配置創建不同類型的線程池,如固定大小的線程池、可緩存的線程池等。通過線程池可以有效地復用線程,減少線程創建和銷毀的開銷,提高系統的性能和響應性。
ScheduledExecutorService接口:用于定時執行任務或周期性執行任務的接口,繼承自ExecutorService接口。
ScheduledThreadPoolExecutor類:ScheduledExecutorService接口的實現類,用于創建定時線程池,可以按照指定的延遲時間或周期執行任務。
性能優化與實踐

在多線程編程中,如何提高程序的性能?

答案:
合理使用線程池:線程池可以復用線程,減少線程創建和銷毀的開銷。根據任務的特點選擇合適的線程池類型,如固定大小的線程池適用于任務數量相對穩定的情況,可緩存的線程池適用于任務數量波動較大的情況。
減少鎖競爭:鎖的競爭會導致線程阻塞和上下文切換,降低程序性能。可以通過優化鎖的粒度,盡量縮小同步代碼塊的范圍,或者使用無鎖的數據結構和算法來避免鎖競爭。
避免線程上下文切換:線程上下文切換會消耗一定的時間和資源。可以通過減少線程的數量、合理安排任務的執行順序、避免不必要的阻塞等方式來減少線程上下文切換的發生。
使用無鎖數據結構:在一些場景下,無鎖數據結構可以提供更高的并發性能,如ConcurrentLinkedQueue、AtomicInteger等。這些數據結構通過使用 CAS 操作等技術來實現無鎖并發訪問,避免了鎖的開銷。
優化線程間通信:合理使用線程間通信機制,如BlockingQueue、CountDownLatch等,避免不必要的等待和喚醒操作,提高線程間的協作效率。
充分利用多核處理器:根據系統的處理器核心數量,合理分配線程數量,使得每個核心都能充分發揮作用,提高系統的并行度。
請描述一個你在實際項目中遇到的多線程問題,以及你是如何解決的。
答案:(以下是一個示例,你可以根據實際情況進行修改和補充)在一個電商項目中,有多個線程同時對商品庫存進行更新操作。由于并發訪問,出現了庫存數據不一致的問題,有些訂單扣除了庫存但沒有更新到數據庫,而有些訂單則更新了多次庫存,導致庫存數量不準確。
解決方法如下:
首先,分析問題的原因是多個線程對庫存的并發更新沒有進行有效的同步控制。
然后,使用ReentrantLock對庫存更新操作進行加鎖,確保在同一時刻只有一個線程能夠更新庫存。在更新庫存的方法中,先獲取鎖,然后進行庫存更新操作,最后釋放鎖。
同時,為了提高性能,對庫存更新的邏輯進行了優化,將一些不必要的操作放在鎖外面執行,只在鎖內部執行關鍵的庫存更新代碼,減少鎖的持有時間。
另外,添加了日志記錄功能,對每次庫存更新操作進行詳細的日志記錄,以便在出現問題時能夠快速定位和排查。
通過以上措施,解決了庫存數據不一致的問題,保證了多線程環境下庫存更新的準確性和穩定性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75299.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75299.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75299.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

unity運行中場景指定模型回放功能(模型是地形并且可以加載預制體進行回放)

回放和加載腳本 using System.Collections.Generic; using UnityEngine;public class TerrainRecorder : MonoBehaviour {[Header("基本設置")]public Terrain targetTerrain;public bool isRecording false;public bool isPlayingBack false;[Range(0.02f, 1f)] …

基于SpringBoot的河道水情大數據可視化分析平臺設計與實現(源碼+論文+部署講解等)

需要資料,請文末聯系 一、平臺介紹 水情監測數據大屏 - 平臺首頁 日均水位 日均水速 二、論文內容 摘要(中文) 本文針對河道水情監測領域的數據管理和可視化分析需求,設計并實現了一套河道水情大數據可視化分析平臺。該平臺基…

Knife4j文檔請求異常 空指針

打開swagger文檔報空指針異常 java.lang.NullPointerException: nullat springfox.documentation.oas.mappers.SchemaMapper.model(SchemaMapper.java:97)at springfox.documentation.oas.mappers.SchemaMapper.mapModel(SchemaMapper.java:85)at springfox.documentation.oas…

車輛選擇解決方案

車輛選擇解決方案 /* * Purpose: 添加車輛選擇的功能 -> 用戶在選擇不同的車輛時,重新初始化系統狀態,清除之前的定時器,并根據新選擇的車輛設置新的定時器,以實現對新車輛狀態的實時加載。 * File Name: 車輛選擇解決方案 * …

魔塔社區使用llamafactory微調AI閱卷試題系統

啟動 LLaMA-Factory 1. 安裝 LLaMA-Factory 執行安裝指令 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e ".[torch,metrics]"解決依賴沖突 如果遇到依賴沖突,可使用以下命令安裝,不…

程序化廣告行業(51/89):Cookie映射與移動設備ID映射解析

程序化廣告行業(51/89):Cookie映射與移動設備ID映射解析 在當今數字化營銷的浪潮中,程序化廣告已經成為企業精準觸達目標客戶的重要手段。作為一名對程序化廣告充滿興趣的學習者,我希望通過這篇博客和大家一起深入探索…

內網服務器centos7安裝jdk17

1. 下載 JDK 17 安裝包(在外網環境操作) 在可聯網的機器上下載 JDK 17 的壓縮包(推薦使用 OpenJDK): OpenJDK 官方源: Adoptium Eclipse Temurin Azul Zulu 直接下載命令示例(在外網機器上執行…

【學Rust寫CAD】21 2D 點(point.rs)

源碼 //matrix/point.rs use std::ops::Mul; use super::algebraic_units::{Zero, One}; use super::generic::Matrix;/// 點坐標結構體 #[derive(Debug, Clone, Copy, PartialEq)] pub struct Point<X, Y>(Matrix<X, Y, One, Zero, Zero, One>);impl<X, Y>…

《AI大模型應知應會100篇》第7篇:Prompt Engineering基礎:如何與大模型有效溝通

第7篇&#xff1a;Prompt Engineering基礎&#xff1a;如何與大模型有效溝通 摘要 Prompt Engineering&#xff08;提示工程&#xff09;是與大模型高效溝通的關鍵技能。通過精心設計的Prompt&#xff0c;可以讓模型生成更準確、更有用的結果。本文將從基礎知識到高級策略&…

Java高頻面試題1:Java SE

一、Java概述 1. Java語言的特點&#xff1f; 面向對象&#xff1a;封裝、繼承、多態。跨平臺&#xff1a;通過JVM實現“一次編寫&#xff0c;到處運行”。內存管理&#xff1a;自動垃圾回收&#xff08;GC&#xff09;&#xff0c;避免手動內存管理。多線程&#xff1a;內置…

基于RapidIO接口的DSP+GPU工業AI實時計算解決方案

基于RapidIO接口的DSPGPU工業AI實時計算解決方案是一種面向高性能、低延遲工業應用的異構計算架構&#xff0c;適用于工業自動化、機器視覺、預測性維護、機器人控制等場景。以下是該方案的核心設計思路和技術要點&#xff1a; 1. 方案背景與目標 工業需求&#xff1a; 工業…

SQL DB 數據類型

SQL DB 數據類型 引言 在數據庫管理系統中,數據類型是定義和存儲數據的方式。SQL(結構化查詢語言)數據庫中的數據類型決定了數據的存儲格式、大小、取值范圍以及如何處理數據。合理選擇和使用數據類型對于確保數據庫性能、數據完整性和應用程序的準確性至關重要。 SQL 數…

常見電源模塊設計

目錄 1. 5V電源模塊 2. 3.3V電源模塊 3. 1.9V電源模塊 4. 220V轉12V電源模塊 1. 5V電源模塊 參考電路 電路說明&#xff1a; 這個電路采用的是穩壓芯片78L05&#xff0c;我是用的12V的電源模塊轉成為5V,為后續的供電。 2. 3.3V電源模塊 參考電路&#xff1a; 電路說明…

python操作es

1、常用操作 ### 創建索引 bash curl -u elastic:123 -X PUT -H "Content-Type: application/json" -d mapping.json "http://0.0.0.0:9200/ai_kg_extraction_new_lower_tag_index" ### 刪除索引 bash curl -u elastic:123 -X DELETE "http://0.0…

記一個.NET AOT交叉編譯時的坑

記一個.NET AOT交叉編譯時的坑 背景&#xff1a; 使用.NET9開發的Avalonia項目需要部署到Linux-arm64 踩坑&#xff1a; 根據官方AOT交叉編譯文檔配置后執行打包 dotnet publish -r linux-arm64提示error : The PrivateSdkAssemblies ItemGroup is required for _ComputeA…

【Linux篇】探索進程地址空間:計算機背后的虛擬世界

進程地址空間的奧秘&#xff1a;讓你理解程序如何在計算機中生存 一. 程序地址空間1.1 基本概念1.2 虛擬內存管理1.3 為什么存在虛擬地址空間1.3.1 意義 2. 最后 本文將介紹進程地址空間的基本概念與結構&#xff0c;幫助讀者理解操作系統如何管理和分配內存。進程地址空間指的…

17查詢文檔的方式

目錄 1.鼠標放在你要查詢的地方或者選中&#xff0c;按FnF1 2Assistant文檔 3幫助菜單界面 1.鼠標放在你要查詢的地方或者選中&#xff0c;按FnF1 2Assistant文檔 3幫助菜單界面 大家一定要有 查詢文檔 的意識!! 未來實際開發中,一定會用到很多的第三方庫和框架的. 很可能用到的…

壹起航:引領中國工廠邁向全球市場的先鋒

在全球化的浪潮中&#xff0c;中國工廠正積極尋求拓展海外市場的新機遇。面對激烈的國際競爭&#xff0c;如何脫穎而出&#xff0c;成為行業翹楚&#xff1f;壹起航憑借其深厚的行業積淀和創新的營銷理念&#xff0c;為中國工廠提供了全方位的出海解決方案。 一、構建國際化外…

“數據導航儀”:企業遷移知識庫如何賦能精準決策

在全球化與區域經濟一體化的浪潮下&#xff0c;企業遷移已成為經濟發展的重要現象。 無論是為了拓展市場、降低成本&#xff0c;還是為了尋找更好的政策環境&#xff0c;企業遷移都牽動著無數從業者的心。 然而&#xff0c;面對海量且分散的企業遷移信息&#xff0c;金融機構…

理解激活函數,多個網絡層之間如何連接

1. 激活函數如何在兩個層之間作用 如果不在兩個層之間添加激活函數&#xff0c;模型將無法學習非線性關系&#xff0c;表現出像線性模型一樣的局限性。 LeakyReLU(0.2) 是一個激活函數&#xff0c;它的作用是對每一層的輸出進行非線性轉換。激活函數通常在神經網絡中用于增加網…