JAVA并發編程3--多線程程序

?

1.創建線程的方法:

案例:計算1-1000的整數和

實現Runnable接口

步驟:
1.創建一個實現了Runnable接口的類
2.實現類去實現Runnable中的抽象方法:run()
3.創建實現類的對象
4.將此對象作為參數傳遞到Thread類的構造器中,創建Thread類的對象
5.通過Thread類的對象調用start() ① 啟動線程 ②調用當前線程的run()–>調用了Runnable類型的target的run()

import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保證線程安全且避免空指針問題public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);public static ExecutorService executor=Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException, ExecutionException {long start = System.currentTimeMillis();Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {MyThread myThread = new MyThread(100);threads[i] = new Thread(myThread);threads[i].start();}countDownLatch.await();System.out.println(result.get()); // 使用 get 方法獲取 AtomicLong 的值System.out.println(System.currentTimeMillis() - start);}
}
class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法進行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}

繼承Thread類

步驟:
1.創建一個繼承于Thread類的子類
2.重寫Thread類的run() --> 將此線程執行的操作聲明在run()中
3.創建Thread類的子類的對象
4.通過此對象調用start()執行線程

import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保證線程安全且避免空指針問題public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);public static ExecutorService executor=Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException, ExecutionException {long start = System.currentTimeMillis();Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {MyThread1 myThread = new MyThread1(100);threads[i] = new Thread(myThread);threads[i].start();}countDownLatch.await();System.out.println(result.get()); // 使用 get 方法獲取 AtomicLong 的值System.out.println(System.currentTimeMillis() - start);}
}
class MyThread1 extends Thread {private long count;MyThread1(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法進行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}

實現Callable接口

步驟:
1.創建一個實現Callable的實現類
2.實現call方法,將此線程需要執行的操作聲明在call()中
3.創建Callable接口實現類的對象
4.將此Callable接口實現類的對象作為傳遞到FutureTask構造器中,創建FutureTask的對象
5.將FutureTask的對象作為參數傳遞到Thread類的構造器中,創建Thread對象,并調用start()
6.獲取Callable中call方法的返回值

package Reflection;import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保證線程安全且避免空指針問題public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);public static void main(String[] args) throws InterruptedException, ExecutionException {
//        long start = System.currentTimeMillis();
//        Thread[] threads = new Thread[count];
//        for (int i = 0; i < count; i++) {
//            MyThread1 myThread = new MyThread1(100);
//            threads[i] = new Thread(myThread);
//            threads[i].start();
//        }
//        countDownLatch.await();
//        System.out.println(result.get()); // 使用 get 方法獲取 AtomicLong 的值
//        System.out.println(System.currentTimeMillis() - start);FutureTask<Integer> futureTask=new FutureTask<>(new MyThread2(100));Thread thread=new Thread(futureTask);thread.start();Integer o = futureTask.get();System.out.println(o);}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法進行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}class MyThread1 extends Thread {private long count;MyThread1(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法進行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}class MyThread2 implements Callable<Integer> {private int count;public MyThread2(int count) {this.count=count;}@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}return sum;}
}

使用線程池

package Reflection;import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保證線程安全且避免空指針問題public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);//線程池public static ExecutorService executor=Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException, ExecutionException {Future<Integer> submit = executor.submit(new MyThread2(100));Integer i=submit.get();System.out.println(i);}
}class MyThread2 implements Callable<Integer> {private int count;public MyThread2(int count) {this.count=count;}@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}return sum;}
}

2.統計多線程程序的運行時間

如果要統計多線程程序的執行時間,主線程等待所有子線程完成,那么總時間可能接近實際所有線程執行完畢的時間。所以可能需要確保主線程在所有子線程結束后才結束,然后計算整個程序的運行時間。

使用join方法

使用thread.join,` 讓一個線程等待另一個線程執行完畢。在多線程編程中,有時我們需要確保某些線程在其他線程完成特定任務后再繼續執行,這時就可以使用 join() 方法來實現線程間的同步。

package Reflection;import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保證線程安全且避免空指針問題public static AtomicLong result = new AtomicLong(0);public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();int count = 10000; // 將線程數量和數組長度統一為 10000Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {MyThread myThread = new MyThread(100);threads[i] = new Thread(myThread);threads[i].start();}for (Thread thread : threads) {thread.join();}System.out.println(result.get()); // 使用 get 方法獲取 AtomicLong 的值System.out.println(System.currentTimeMillis() - start);}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法進行原子更新ThreadLearn.result.addAndGet(sum);}
}

在這里插入圖片描述

使用CountDownLatch

CountDownLatch 是 Java 并發包 java.util.concurrent 中的一個同步輔助類,主要用于協調多個線程之間的執行順序。它可以讓一個或多個線程等待其他一組線程完成它們的操作后再繼續執行,從而確保程序的執行邏輯按照預期進行,避免出現數據不一致或邏輯錯誤的問題。

package Reflection;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保證線程安全且避免空指針問題public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {MyThread myThread = new MyThread(100);threads[i] = new Thread(myThread);threads[i].start();}countDownLatch.await();System.out.println(result.get()); // 使用 get 方法獲取 AtomicLong 的值System.out.println(System.currentTimeMillis() - start);}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法進行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}

執行結果:
在這里插入圖片描述

CountDownLatch 的執行過程
1. 初始化階段
  • 首先,需要創建一個 CountDownLatch 實例,在創建時要傳入一個初始計數值。這個計數值代表了需要等待完成的操作數量。例如在代碼中 CountDownLatch countDownLatch = new CountDownLatch(count);,這里的 count 就是初始計數值,它表明有 count 個任務需要完成,CountDownLatch 會將這個值存儲在內部作為計數器的初始狀態。
2. 任務線程啟動階段
  • 通常會有一個或多個任務線程被創建并啟動去執行特定的任務。這些線程可能會并發地執行各自的任務,就像代碼里通過循環創建并啟動多個 Thread 實例,每個線程都去執行 MyThread 類的 run 方法中的任務。
3. 等待階段
  • 有一個或多個線程(通常是主線程)會調用 CountDownLatchawait() 方法。當調用這個方法時,調用線程會進入阻塞狀態,它會一直等待,直到 CountDownLatch 的內部計數器值變為 0。這意味著它在等待所有任務都完成。
4. 任務完成與計數器遞減階段
  • 每個任務線程在完成自己的任務后,會調用 CountDownLatchcountDown() 方法。這個方法的作用是將 CountDownLatch 的內部計數器值減 1。隨著越來越多的任務線程完成任務并調用 countDown() 方法,計數器的值會不斷減小。
5. 喚醒等待線程階段
  • CountDownLatch 的內部計數器值減到 0 時,所有之前調用 await() 方法并處于阻塞狀態的線程會被喚醒。這些線程會從 await() 方法處繼續執行后續的代碼邏輯。
6. 后續處理階段
  • 被喚醒的線程可以繼續執行后續的操作,比如代碼中主線程在被喚醒后會打印最終的計算結果以及程序執行所花費的時間。
主要方法
  • CountDownLatch(int count) 構造方法:用于創建一個 CountDownLatch 實例,并初始化其內部計數器的值為 count。這個 count 表示需要等待完成的操作數量,必須是一個正整數。
  • void await() 方法:調用該方法的線程會進入阻塞狀態,直到 CountDownLatch 的內部計數器值變為 0。如果在等待過程中當前線程被中斷,會拋出 InterruptedException 異常。
  • boolean await(long timeout, TimeUnit unit) 方法:調用該方法的線程會等待一段時間,最多等待 timeout 時間(由 unit 指定時間單位)。如果在這段時間內計數器值變為 0,則線程會被喚醒并返回 true;如果超過指定時間計數器值仍不為 0,則線程會被喚醒并返回 false。同樣,如果在等待過程中線程被中斷,會拋出 InterruptedException 異常。
  • void countDown() 方法:該方法會將 CountDownLatch 的內部計數器值減 1。當計數器值減到 0 時,所有正在等待的線程會被喚醒。如果計數器值已經為 0,調用該方法不會產生任何效果。
  • long getCount() 方法:該方法用于返回 CountDownLatch 當前的計數器值。可以通過這個方法來查看還有多少個任務未完成。

3.如何解決多線程對共享變量操作的線程安全問題

package Reflection;public class ThreadLearn {public static long result ;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10000; i++) {MyThread myThread = new MyThread(1000);Thread thread = new Thread(myThread);thread.start();}Thread.sleep(10*1000);System.out.println(result);}}
class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum+=i;}ThreadLearn.result+=sum;System.out.println(sum);}
}

執行結果:
??在這里插入圖片描述

代碼功能概述

這段 Java 代碼主要的功能是創建 10000 個線程,每個線程會計算從 0 到 1000 的整數之和,然后將這個和累加到 ThreadLearn 類的靜態變量 result 中。最后,主線程等待 10 秒后輸出 result 的值。

代碼存在的問題

  1. 線程安全問題ThreadLearn.result 是一個共享變量,多個線程同時對其進行寫操作(ThreadLearn.result += sum;),這會導致數據競爭(Data Race)問題,最終的 result 值可能是錯誤的。
  2. 等待時間不確定性:使用 Thread.sleep(10 * 1000) 來等待所有子線程完成,這種方式不夠可靠,因為不同的機器性能不同,可能會導致有些線程還未執行完,主線程就已經輸出結果。

你提供的代碼在多線程環境下存在線程安全問題,主要是因為多個線程同時對靜態變量 ThreadLearn.result 進行寫操作,這可能會導致數據競爭和不一致的結果。下面為你介紹幾種解決該問題的方法。

方法一:使用 synchronized 關鍵字

synchronized 關鍵字可以用來修飾方法或代碼塊,保證同一時刻只有一個線程能夠訪問被修飾的代碼,從而避免多線程對共享資源的并發訪問問題。

package Reflection;public class ThreadLearn {public static long result;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10000; i++) {MyThread myThread = new MyThread(1000);Thread thread = new Thread(myThread);thread.start();}Thread.sleep(10 * 1000);System.out.println(result);}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 synchronized 塊保證線程安全synchronized (ThreadLearn.class) {ThreadLearn.result += sum;}System.out.println(sum);}
}

解釋:在 run 方法中,使用 synchronized (ThreadLearn.class)ThreadLearn.result += sum; 這行代碼進行同步,這樣同一時刻只有一個線程能夠執行該代碼塊,從而保證了對 result 變量的線程安全訪問。

執行結果:
在這里插入圖片描述

方法二:使用 AtomicLong 類

AtomicLong 是 Java 提供的一個原子類,它提供了一些原子操作方法,可以保證對長整型變量的原子性更新,避免了使用 synchronized 帶來的性能開銷。

package Reflection;import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 代替 longpublic static AtomicLong result = new AtomicLong(0);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10000; i++) {MyThread myThread = new MyThread(1000);Thread thread = new Thread(myThread);thread.start();}Thread.sleep(10 * 1000);System.out.println(result.get());}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法進行原子更新result.addAndGet(sum);System.out.println(sum);}
}

解釋:將 ThreadLearn 類中的 result 變量類型改為 AtomicLong,并使用 addAndGet 方法來更新 result 的值。addAndGet 方法是原子操作,能夠保證在多線程環境下對 result 的更新是線程安全的。

執行結果:
在這里插入圖片描述

在這里插入圖片描述

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

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

相關文章

django中間件,中間件給下面傳值

1、新建middleware.py文件 # myapp/middleware.py import time from django.http import HttpRequest import json from django.http import JsonResponse import urllib.parse class RequestTimeMiddleware:def __init__(self, get_response):self.get_response get_respons…

Vision Transformer:打破CNN壟斷,全局注意力機制重塑計算機視覺范式

目錄 引言 一、ViT模型的起源和歷史 二、什么是ViT&#xff1f; 圖像處理流程 圖像切分 展平與線性映射 位置編碼 Transformer編碼器 分類頭&#xff08;Classification Head&#xff09; 自注意力機制 注意力圖 三、Coovally AI模型訓練與應用平臺 四、ViT與圖像…

深入淺出:探索 DeepSeek 的強大功能與應用

深入淺出&#xff1a;探索 DeepSeek 的強大功能與應用 在人工智能技術飛速發展的今天&#xff0c;自然語言處理&#xff08;NLP&#xff09;作為其重要分支&#xff0c;正逐漸滲透到我們生活的方方面面。DeepSeek 作為一款功能強大的 NLP 工具&#xff0c;憑借其易用性和高效性…

為AI聊天工具添加一個知識系統 之97 詳細設計之38 Token 之3 前端開發

本文要點 要點 本項目&#xff08;為使用各種聊天工具的聊天者 加掛一個知識系統&#xff09;前端 的用戶界面上 主要 有 知識圖譜、認知地圖和語義網絡&#xff0c;和 聊天工具的chat嵌入 同時還必須有知識樹 成熟的果實&#xff08;對外的網頁 ) 以及 組建和參與利益相關組…

輪子項目--消息隊列的實現(3)

上一篇文章中我把一些關鍵的類以及表示出來&#xff0c;如何對這些類對應的對象進行管理呢&#xff1f;管理分為硬盤和內存上&#xff0c;硬盤又分為數據庫&#xff08;管理交換機&#xff0c;隊列和綁定&#xff09;和文件&#xff08;管理消息&#xff09;&#xff0c;本文就…

Svelte前端框架

Svelte 簡介 Svelte 是一個現代的前端框架&#xff0c;用于構建高效、響應式的用戶界面。與 React、Vue 和 Angular 等傳統框架不同&#xff0c;Svelte 在構建時將組件編譯為高效的純 JavaScript 代碼&#xff0c;而不是在瀏覽器中運行一個龐大的運行時庫。這使得 Svelte 應用具…

【轉載】開源鴻蒙OpenHarmony社區運營報告(2025年1月)

●截至2025年1月31日&#xff0c;開放原子開源鴻蒙&#xff08;OpenAtom OpenHarmony&#xff0c;簡稱“開源鴻蒙”或“OpenHarmony”&#xff09;社區累計超過8200名貢獻者&#xff0c;共63家成員單位&#xff0c;產生51.2萬多個PR、2.9萬多個Star、10.5萬多個Fork、68個SIG。…

@SneakyThrows:是Java異常處理的“魔法外掛“,還是隱藏的“定時炸彈“?

引言&#xff1a;當Java的異常機制成為"甜蜜的負擔" Java的檢查型異常&#xff08;Checked Exception&#xff09;設計本意是提升代碼健壯性&#xff0c;但開發者常常陷入兩難&#xff1a; 要么用try-catch層層包裹代碼導致"金字塔噩夢"&#xff0c;要么在…

雙周報Vol.65:新增is表達式、字符串構造和數組模式匹配增強、IDE模式匹配補全增強...多項技術更新!

MoonBit更新 新增 is 表達式 這個表達式的語法形式為 expr is pat&#xff0c;這個表達式為 Bool 類型&#xff0c;當 expr 符合 pat 這個模式的時候返回 true&#xff0c;比如&#xff1a; fn use_is_expr(x: Int?) -> Unit {if x is Some(i) && i > 10 { .…

Git 與持續集成 / 持續部署(CI/CD)的集成

一、引言 在當今快速發展的軟件開發領域&#xff0c;高效的代碼管理和持續的交付流程是項目成功的關鍵因素。Git 作為一款分布式版本控制系統&#xff0c;已經成為了開發者們管理代碼的標配工具&#xff1b;而持續集成 / 持續部署&#xff08;CI/CD&#xff09;則是一種能夠加…

百問網imx6ullpro調試記錄(linux+qt)

調試記錄 文章目錄 調試記錄進展1.開發板相關1.1百問網烏班圖密碼 1.2 換設備開發環境搭建串口調試網絡互通nfs文件系統掛載 1.3網絡問題1.4系統啟動1.5進程操作 2.QT2.1tslib1.獲取源碼2.安裝依賴文件3.編譯 2.2qt移植1.獲取qt源碼2.配置編譯器3.編譯 2.3拷貝到開發板1.拷貝2.…

開發中用到的設計模式

目錄 開發中用到的設計模式 工廠模式 設計理念 好處 體現的編程思想 適配器模式 概念 策略模式和適配器模式的區別 選擇策略模式而非適配器模式的原因 設計模式的開發原則 開發中用到的設計模式 在開發過程中&#xff0c;常見的設計模式會根據不同的業務場景和需求被…

1064 - You have an error in your SQL syntax;

在創建數據庫表建立外鍵是遇到了如下報錯 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near position(position_id) ) at line 8 數據庫表sql如下&#xff1a; --職位表 CR…

無人機 ,遙控器與接收機之前的通信

目錄 1、信號類型 2、工作頻率 3、通信協議 3.1、PPM 協議 3.2、SBUS 協議 3.3、CRSF 協議 無人機的遙控器和接收機之間常用的信號、頻率、協議等相關信息如下&#xff1a; 1、信號類型 模擬信號 特點&#xff1a;信號的幅度、頻率或相位等參數是連續變化的&#xff0c…

【c++】四種類型轉換形式

【c】四種類型轉換形式 編譯時: static_cast&#xff08;靜態轉換&#xff09; const_cast&#xff08;去常性轉換&#xff09; reinterpret_cast&#xff08;重新解釋轉換&#xff0c;直接轉換地址&#xff09; 運行時: dynamic_cast&#xff08;動態轉換&#xff0c;運行時類…

Cisco ASR1002查看資源占用的幾條命令

查看平臺資源 show platform resource 該命令用于顯示整個平臺的資源使用情況&#xff0c;包括 CPU、內存等 example: ASR1002# show platform resources **State Acronym: H - Healthy, W - Warning, C - Critical Resource…

Day 1:認知革命與DeepSeek生態定位

目標&#xff1a;建立對大模型技術范式的系統性認知&#xff0c;掌握DeepSeek的核心技術特性與生態價值 一、大模型技術演進&#xff1a;從GPT到DeepSeek 1.1 技術發展里程碑 2017-Transformer突破&#xff1a;Self-Attention機制如何突破RNN的序列建模瓶頸 2018-GPT初代&…

Python自動化辦公之Excel拆分

在日常辦公中&#xff0c;我們經常需要將包含多個Sheet頁的Excel文件拆分成多個獨立的Excel文件。例如&#xff0c;在發送Excel表給各部門確認時&#xff0c;出于控制知悉范圍最小等保密性考慮&#xff0c;每個部門只需要查看和確認自己部門對應的Sheet頁。手動拆分Excel文件非…

【CXX-Qt】1.1 Rust中的QObjects

本文涉及到了使用CXX-Qt將Rust、C和QML集成到Qt應用程序中的各個方面。下面&#xff0c;我將提供一個簡單的示例&#xff0c;演示如何使用CXX-Qt來創建一個Rust結構體并將其作為QObject子類暴露給C和QML。 一、設置CXX-Qt環境 首先&#xff0c;確保您已經安裝了Rust、CXX和CX…

Conda命令整理

Conda 是一個功能強大的包和環境管理工具&#xff0c;廣泛用于 Python 開發中。除了基本的包和環境管理功能外&#xff0c;Conda 還提供了許多高級用法和技巧&#xff0c;幫助用戶更高效地管理和維護 Python 環境。 1. 管理 Conda 本身 命令描述示例conda --version查看 Cond…