[java八股文][Java并發編程面試篇]場景

多線程打印奇偶數,怎么控制打印的順序

可以利用wait()和notify()來控制線程的執行順序。

以下是一個基于這種方法的簡單示例:

public class PrintOddEven {private static final Object lock = new Object();private static int count = 1;private static final int MAX_COUNT = 10;public static void main(String[] args) {Runnable printOdd = () -> {synchronized (lock) {while (count <= MAX_COUNT) {if (count % 2 != 0) {System.out.println(Thread.currentThread().getName() + ": " + count++);lock.notify();} else {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}};Runnable printEven = () -> {synchronized (lock) {while (count <= MAX_COUNT) {if (count % 2 == 0) {System.out.println(Thread.currentThread().getName() + ": " + count++);lock.notify();} else {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}};Thread oddThread = new Thread(printOdd, "OddThread");Thread evenThread = new Thread(printEven, "EvenThread");oddThread.start();evenThread.start();}
}

在上面的示例中,通過一個共享的鎖對象lock來控制兩個線程的交替執行。一個線程負責打印奇數,另一個線程負責打印偶數,通過wait()和notify()方法來在兩個線程之間實現順序控制。當當前應該打印奇數時,偶數線程會進入等待狀態,反之亦然。

  • 創建 3 個并發執行的線程,在每個線程的任務結束時調用?countDown?方法將計數器減 1。
  • 創建第 4 個線程,使用?await?方法等待計數器為 0,即等待其他 3 個線程完成任務。
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) {// 創建一個 CountDownLatch,初始計數為 3CountDownLatch latch = new CountDownLatch(3);// 創建并啟動 3 個并發線程for (int i = 0; i < 3; i++) {final int threadNumber = i + 1;new Thread(() -> {try {System.out.println("Thread " + threadNumber + " is working.");// 模擬線程執行任務Thread.sleep((long) (Math.random() * 1000));System.out.println("Thread " + threadNumber + " has finished.");} catch (InterruptedException e) {e.printStackTrace();} finally {// 任務完成后,計數器減 1latch.countDown();}}).start();}// 創建并啟動第 4 個線程,等待其他 3 個線程完成new Thread(() -> {try {System.out.println("Waiting for other threads to finish.");// 等待計數器為 0latch.await();System.out.println("All threads have finished, this thread starts to work.");} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}

代碼解釋

  • 首先,創建了一個?CountDownLatch?對象?latch,并將其初始計數設置為 3。
  • 然后,使用?for?循環創建并啟動 3 個線程。每個線程會執行一些工作(這里使用?Thread.sleep?模擬),在工作完成后,會調用?latch.countDown()?方法,將?latch?的計數減 1。
  • 最后,創建第 4 個線程。這個線程在開始時調用?latch.await()?方法,它會阻塞,直到?latch?的計數為 0,即前面 3 個線程都調用了?countDown()?方法。一旦計數為 0,該線程將繼續執行后續任務。

#單例模型既然已經用了synchronized,為什么還要在加volatile?

使用?synchronized?和?volatile?一起,可以創建一個既線程安全又能正確初始化的單例模式,避免了多線程環境下的各種潛在問題。這是一種比較完善的線程安全的單例模式實現方式,尤其適用于高并發環境。

public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

synchronized?關鍵字的作用用于確保在多線程環境下,只有一個線程能夠進入同步塊(這里是?synchronized (Singleton.class))。在創建單例對象時,通過?synchronized?保證了創建過程的線程安全性,避免多個線程同時創建多個單例對象。

volatile?確保了對象引用的可見性和創建過程的有序性,避免了由于指令重排序而導致的錯誤。

instance = new Singleton();?這行代碼并不是一個原子操作,它實際上可以分解為以下幾個步驟:

  • 分配內存空間。
  • 實例化對象。
  • 將對象引用賦值給?instance

由于 Java 內存模型允許編譯器和處理器對指令進行重排序,在沒有?volatile?的情況下,可能會出現重排序,例如先將對象引用賦值給?instance,但對象的實例化操作尚未完成。

這樣,其他線程在檢查?instance == null?時,會認為單例已經創建,從而得到一個未完全初始化的對象,導致錯誤。

volatile?可以保證變量的可見性和禁止指令重排序。它確保對?instance?的修改對所有線程都是可見的,并且保證了上述三個步驟按順序執行,避免了在單例創建過程中因指令重排序而導致的問題。

#3個線程并發執行,1個線程等待這三個線程全部執行完在執行,怎么實現?

可以使用?CountDownLatch?來實現 3 個線程并發執行,另一個線程等待這三個線程全部執行完再執行的需求。以下是具體的實現步驟:

  • 創建一個?CountDownLatch?對象,并將計數器初始化為 3,因為有 3 個線程需要等待。
  • 創建 3 個并發執行的線程,在每個線程的任務結束時調用?countDown?方法將計數器減 1。
  • 創建第 4 個線程,使用?await?方法等待計數器為 0,即等待其他 3 個線程完成任務。
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) {// 創建一個 CountDownLatch,初始計數為 3CountDownLatch latch = new CountDownLatch(3);// 創建并啟動 3 個并發線程for (int i = 0; i < 3; i++) {final int threadNumber = i + 1;new Thread(() -> {try {System.out.println("Thread " + threadNumber + " is working.");// 模擬線程執行任務Thread.sleep((long) (Math.random() * 1000));System.out.println("Thread " + threadNumber + " has finished.");} catch (InterruptedException e) {e.printStackTrace();} finally {// 任務完成后,計數器減 1latch.countDown();}}).start();}// 創建并啟動第 4 個線程,等待其他 3 個線程完成new Thread(() -> {try {System.out.println("Waiting for other threads to finish.");// 等待計數器為 0latch.await();System.out.println("All threads have finished, this thread starts to work.");} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}

代碼解釋

  • 首先,創建了一個?CountDownLatch?對象?latch,并將其初始計數設置為 3。
  • 然后,使用?for?循環創建并啟動 3 個線程。每個線程會執行一些工作(這里使用?Thread.sleep?模擬),在工作完成后,會調用?latch.countDown()?方法,將?latch?的計數減 1。
  • 最后,創建第 4 個線程。這個線程在開始時調用?latch.await()?方法,它會阻塞,直到?latch?的計數為 0,即前面 3 個線程都調用了?countDown()?方法。一旦計數為 0,該線程將繼續執行后續任務。

#假設兩個線程并發讀寫同一個整型變量,初始值為零,每個線程加 50 次,結果可能是什么?

在沒有任何同步機制的情況下,兩個線程并發對同一個整型變量進行 50 次加 1 操作,最終結果可能是 100,也可能小于 100,最壞的結果是 50,也就是最終的結果可能是在 [50, 100] 。

小于 100 情況的分析,由于對整型變量的?num++?操作不是原子操作,它實際上包含了三個步驟:讀取變量的值、將值加 1、將新值寫回變量。在多線程環境下,可能會出現線程安全問題。例如,線程 1 和線程 2 同時讀取了變量的當前值,然后各自將其加 1,最后都將相同的新值寫回變量,這就導致了一次加 1 操作的丟失。這種情況會多次發生,最終結果就會小于 100。

import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerAddition {private static AtomicInteger num = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {for (int i = 0; i < 50; i++) {num.incrementAndGet();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 50; i++) {num.incrementAndGet();}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("最終結果: " + num.get());}
}

第二種方式:通過?synchronized?關鍵字或?ReentrantLock?確保操作的互斥性,代碼如下:

public class SynchronizedAddition {private static int num = 0;private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {for (int i = 0; i < 50; i++) {synchronized (lock) {num++;}}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 50; i++) {synchronized (lock) {num++;}}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("最終結果: " + num);}
}

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

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

相關文章

MySQL的索引和事務

目錄 1、索引 1.1 查看索引 1.2 創建索引 1.3 刪除索引 1.4 索引的實現 2、事務 1、索引 索引等同于目錄&#xff0c;屬于針對查詢操作的一個優化手段&#xff0c;可以通過索引來加快查詢的速度&#xff0c;避免針對表進行遍歷。 主鍵、unique和外鍵都是會自動生成索引的…

Qt 驗證自動釋放 + 亂碼問題(6)

文章目錄 驗證自動釋放&#xff08;對象樹上的對象&#xff09;亂碼問題的緣由解決亂碼問題1. 使用QString2. qDebug() 小結 簡介&#xff1a;上一篇文章寫到&#xff0c;當new出一個控件對象并且將它掛到對象樹上&#xff0c;無需我們手動釋放該對象&#xff0c;是因為在一個合…

fastjson2 json.tojsonstring 會自動忽略過濾掉 key: null的數據

如果你想在序列化時保留值為 null 的字段&#xff0c;只要打開 Fastjson2 的 WriteNulls 特性即可。常見做法有兩種——按調用級別開啟&#xff0c;或全局開啟。 1. 在每次序列化時加 WriteNulls import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONWriter…

LeetCode熱題100--54.螺旋矩陣--中等

1. 題目 給你一個 m 行 n 列的矩陣 matrix &#xff0c;請按照 順時針螺旋順序 &#xff0c;返回矩陣中的所有元素。 示例 1&#xff1a; 輸入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 輸出&#xff1a;[1,2,3,6,9,8,7,4,5] 示例 2&#xff1a; 輸入&#xff1a;ma…

別卷手柄了!跨平臺VR遙操系統實現仿真

我們構建了一個基于 Quest 3 的 VR 遙操系統&#xff0c;該系統能夠同時支持 DISCOVERSE 仿真環境與 MMK2 真機的操控&#xff0c;實現了從虛擬環境到真實機器人系統的無縫對接。 ? 基于 VR 實現的遙操系統具有良好的擴展性和便攜性&#xff0c;為多場景應用提供了靈活的操作方…

Linux復習筆記(一)基礎命令和操作

遇到的問題&#xff0c;都有解決方案&#xff0c;希望我的博客能為你提供一點幫助。 一、Linux中的基礎命令和操作&#xff08;約30%-40%) 1.用戶和組&#xff08;5%左右&#xff09; 1.1用戶簡介&#xff08;了解&#xff09; 要求&#xff1a;了解&#xff0c;知道有三個用戶…

【C語言干貨】野指針

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、什么是野指針&#xff1f;二、野指針的三大成因 1.指針未初始化2.指針越界訪問2.指針指向已釋放的內存 前言 提示&#xff1a;以下是本篇文章正文內容&…

Unity:Surface Effector 2D(表面效應器 2D)

目錄 什么是表面效應器 2D&#xff1f; &#x1f3af; 它是做什么的&#xff1f; &#x1f9ea; 從第一性原理解釋它是怎么工作的 &#x1f4e6; 重要參數解釋 為什么不直接用 Rigidbody&#xff08;剛體&#xff09;來控制運動 &#xff1f; 所以什么時候該用哪個&#…

android 記錄應用內存

windows cmd下 30s 記錄一次 echo off setlocal enabledelayedexpansion set /a counter0 :loop echo %date% %time% >> meminfo.txt adb shell dumpsys meminfo packagename>> meminfo.txt timeout /t 30 /nobreak >nul set /a counter1 echo counter: !coun…

基于神經網絡的 YOLOv8、MobileNet、HigherHRNet 姿態檢測比較研究

摘要 隨著人工智能技術的飛速發展&#xff0c;基于神經網絡的姿態檢測技術在計算機視覺領域取得了顯著進展。本文旨在深入比較分析當前主流的姿態檢測模型&#xff0c;即 YOLOv8、MobileNet 和 HigherHRNet&#xff0c;從模型架構、性能表現、應用場景等多維度展開研究。通過詳…

解決HomeAssistant 無法安裝 samba share問題

最近家里樹莓派上的homeassistant 被折騰崩了&#xff0c;重新安裝過程中發現加載項“Official add-ons”里面的“samba share”、“file edit”、“Mosquitto broker”等常用組件都不能安裝。報以下錯誤&#xff1a; [supervisor.docker.interface] Cant install homeassista…

[Linux]從零開始的STM32MP157 Buildroot根文件系統構建

一、前言 在前面的教程中&#xff0c;教了大家如何移植一個LInux的內核并且正確啟動&#xff0c;我們發現Linux內核在啟動后會出現一個錯誤&#xff0c;提示我們沒有找到根文件系統。那么什么是根文件系統呢&#xff1f;之前我們使用Ubuntu編譯了STM32MP157的TF-A,UBOOT,LINUX內…

2025-05-07 學習記錄--Python-變量 + 常量 + 命名規則 + 變量的數據類型 + 數據類型

合抱之木&#xff0c;生于毫末&#xff1b;九層之臺&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、變量 ?? 多個變量的賦值 &#x1f36d; 二、常量 ?? 程序在運行的過程中&#xff0c;值永遠不會發生改變的量稱之為常量…

16.狀態模式:思考與解讀

原文地址:狀態模式&#xff1a;思考與解讀 更多內容請關注&#xff1a;深入思考與解讀設計模式 引言 在開發軟件系統時&#xff0c;特別是當對象的行為會隨著狀態的變化而變化時&#xff0c;系統往往會變得復雜。你是否遇到過這樣的情況&#xff1a;一個對象的行為在不同的狀…

從 Pretrain 到 Fine-tuning:大模型遷移學習的核心原理剖析

引言 在人工智能領域&#xff0c;大模型的出現掀起了一場技術革命。這些擁有海量參數的模型&#xff0c;如 GPT-4、PaLM 等&#xff0c;在眾多任務上展現出了驚人的能力。然而&#xff0c;訓練一個大模型需要耗費巨大的計算資源和時間&#xff0c;而且直接讓大模型處理特定領域…

Java詳解LeetCode 熱題 100(11):LeetCode 239. 滑動窗口最大值(Sliding Window Maximum)詳解

文章目錄 1. 題目描述2. 理解題目3. 解法一&#xff1a;暴力法3.1 思路3.2 Java代碼實現3.3 代碼詳解3.4 復雜度分析3.5 適用場景 4. 解法二&#xff1a;優先隊列&#xff08;最大堆&#xff09;4.1 思路4.2 Java代碼實現4.3 代碼詳解4.4 復雜度分析4.5 適用場景 5. 解法三&…

org.apache.poi——將 office的各種類型文件(word等文件類型)轉為 pdf

org.apache.poi——將 office的各種類型文件&#xff08;word等文件類型&#xff09;轉為 pdf 簡介使用方法word轉pdf 使用示例word轉pdf 簡介 使用方法 word轉pdf Maven坐標為 <dependency><groupId>com.documents4j</groupId><artifactId>documen…

二叉樹與優先級隊列

1.樹 樹是由n個數據構成的非線性結構&#xff0c;它是根朝上&#xff0c;葉朝下。 注意&#xff1a;樹形結構之中&#xff0c;子樹之間不能連接&#xff0c;不然就不構成樹形結構 1.子樹之間沒有交集 2.除了根節點以外&#xff0c;每一個節點有且只有一個父親節點 3.一個n個…

如何進行室內VR全景拍攝?

如何進行室內VR全景拍攝&#xff1f; 室內VR全景拍攝作為先進的視覺技術&#xff0c;能夠為用戶提供沉浸式的空間體驗。本文介紹如何進行室內VR全景拍攝&#xff0c;并闡述眾趣科技在這一領域的技術支持和服務優勢。 室內VR全景拍攝基礎 1. 室內VR全景拍攝概述 室內VR全景拍…

如何通過代理 IP 實現異地直播推流

在直播行業日益火爆的今天&#xff0c;許多主播希望突破地域限制&#xff0c;實現異地直播推流&#xff0c;以獲得更廣泛的觀眾群體和更好的直播效果。代理 IP 作為一種有效的網絡工具&#xff0c;能夠幫助主播輕松達成這一目標。本文將詳細介紹如何通過代理 IP 實現異地直播推…