建設(shè)pc端網(wǎng)站是什么意思網(wǎng)絡(luò)策劃書(shū)范文
本文主要介紹了Java中的并發(fā)編程模型和常用工具類,首先闡述了并發(fā)編程的概念及其重要性,然后詳細(xì)介紹了線程的基本概念、生命周期和狀態(tài)轉(zhuǎn)換、同步與互斥、死鎖問(wèn)題以及線程池的使用和實(shí)現(xiàn)原理。接著介紹了synchronized關(guān)鍵字和Lock接口的使用、原子變量和原子操作類的使用、Condition接口和ReentrantLock類的使用、CountDownLatch類和CyclicBarrier類的使用、Semaphore類和Exchanger類的使用。最后,提出了并發(fā)編程的性能優(yōu)化和注意事項(xiàng)。
一. 引言
1.1 并發(fā)編程的概念及重要性
并發(fā)編程指的是在多核心、多線程、多任務(wù)的操作系統(tǒng)中,同時(shí)執(zhí)行多個(gè)任務(wù)和線程。在計(jì)算機(jī)領(lǐng)域中,如今大多數(shù)系統(tǒng)都支持并發(fā)編程,因?yàn)椴l(fā)編程可以大大提高系統(tǒng)的吞吐量和響應(yīng)速度,提升系統(tǒng)的性能和可用性。Java作為一門面向?qū)ο蟮木幊陶Z(yǔ)言,也提供了一套完善的并發(fā)編程模型和工具類庫(kù),為Java開(kāi)發(fā)者提供了便捷的并發(fā)編程解決方案。
1.2 Java中的并發(fā)編程模型和常用工具類簡(jiǎn)介
Java中的并發(fā)編程模型基于線程和鎖機(jī)制。Java中的線程是輕量級(jí)的進(jìn)程,每個(gè)線程都有自己的執(zhí)行路徑,可以獨(dú)立執(zhí)行任務(wù)。Java中的鎖機(jī)制可以保證多個(gè)線程之間的同步和互斥,避免資源競(jìng)爭(zhēng)和沖突。Java中的線程和鎖機(jī)制是Java并發(fā)編程的基礎(chǔ)。
- synchronized關(guān)鍵字和Lock接口的使用 synchronized關(guān)鍵字和Lock接口是Java中的兩種鎖機(jī)制,它們可以保證多個(gè)線程之間的同步和互斥。synchronized關(guān)鍵字是Java中最常用的同步機(jī)制,它可以用來(lái)修飾方法或代碼塊。Lock接口是Java中提供的另一種同步機(jī)制,它可以實(shí)現(xiàn)更細(xì)粒度的鎖定,支持可中斷、可重入等高級(jí)特性。
- 原子變量和原子操作類的使用 Java中的原子變量和原子操作類可以保證數(shù)據(jù)的原子性,避免多線程之間的競(jìng)爭(zhēng)和沖突。Java中的原子操作類包括AtomicInteger、AtomicLong、AtomicBoolean等,它們都提供了一系列原子操作方法,例如incrementAndGet、decrementAndGet等,可以實(shí)現(xiàn)原子性的數(shù)值計(jì)算。
- Condition接口和ReentrantLock類的使用 Condition接口和ReentrantLock類是Java中用于高級(jí)同步機(jī)制的工具類。Condition接口是ReentrantLock類的一部分,可以用來(lái)實(shí)現(xiàn)更高級(jí)別的條件等待和通知機(jī)制。ReentrantLock類是Java中提供的另一種同步機(jī)制,可以實(shí)現(xiàn)更細(xì)粒度的鎖定和高級(jí)特性,例如可重入、公平性、可中斷等。
- CountDownLatch類和CyclicBarrier類的使用 CountDownLatch類和CyclicBarrier類是Java中用于同步多線程的工具類。
二. Java中的并發(fā)編程模型
2.1 進(jìn)程和線程的基本概念
進(jìn)程和線程是計(jì)算機(jī)操作系統(tǒng)中的兩個(gè)基本概念,它們都可以執(zhí)行任務(wù)和程序,但是在實(shí)現(xiàn)方式、資源占用、通信方式等方面有所不同。
1. 進(jìn)程
進(jìn)程是計(jì)算機(jī)中執(zhí)行任務(wù)的基本單位,它是操作系統(tǒng)中的一個(gè)獨(dú)立的運(yùn)行實(shí)例。每個(gè)進(jìn)程都有自己的獨(dú)立地址空間、代碼段、數(shù)據(jù)段、堆棧、文件描述符等系統(tǒng)資源。進(jìn)程之間相互獨(dú)立,互不干擾。進(jìn)程是資源分配的最小單位,它可以分配和使用計(jì)算機(jī)中的資源,如CPU、內(nèi)存、文件、網(wǎng)絡(luò)等。
2. 線程
線程是進(jìn)程中的一個(gè)執(zhí)行單元,它是操作系統(tǒng)中調(diào)度的最小單位。一個(gè)進(jìn)程中可以包含多個(gè)線程,這些線程共享進(jìn)程的地址空間和系統(tǒng)資源,如文件、網(wǎng)絡(luò)等。線程之間可以通過(guò)共享內(nèi)存的方式進(jìn)行通信,但是也可能出現(xiàn)競(jìng)爭(zhēng)和沖突的情況。線程是輕量級(jí)的進(jìn)程,創(chuàng)建和銷毀線程的開(kāi)銷相對(duì)較小,因此可以更加高效地利用計(jì)算機(jī)的資源。
3. 進(jìn)程和線程的區(qū)別
進(jìn)程和線程的主要區(qū)別在于資源占用、調(diào)度和通信方式等方面。進(jìn)程是資源分配的最小單位,它可以分配和使用計(jì)算機(jī)中的資源,但是進(jìn)程之間的通信需要通過(guò)IPC(Inter-Process Communication)機(jī)制,開(kāi)銷相對(duì)較大。線程是輕量級(jí)的進(jìn)程,它共享進(jìn)程的地址空間和系統(tǒng)資源,因此線程之間的通信和調(diào)度開(kāi)銷相對(duì)較小。但是線程之間可能出現(xiàn)競(jìng)爭(zhēng)和沖突的情況,需要進(jìn)行同步和互斥操作。
2.2 線程的生命周期和狀態(tài)轉(zhuǎn)換
線程的生命周期包括五種狀態(tài):新建狀態(tài)、就緒狀態(tài)、運(yùn)行狀態(tài)、阻塞狀態(tài)和死亡狀態(tài)。這些狀態(tài)可以通過(guò)不同的方法進(jìn)行轉(zhuǎn)換,下面是每個(gè)狀態(tài)的含義、轉(zhuǎn)換條件:
- 新建狀態(tài)(New) 線程被創(chuàng)建但是還沒(méi)有開(kāi)始運(yùn)行的狀態(tài),此時(shí)線程并沒(méi)有分配到CPU資源,只是一個(gè)空殼子。新建狀態(tài)的線程可以通過(guò)start()方法啟動(dòng)。
- 就緒狀態(tài)(Runnable) 線程被分配到CPU資源并且可以運(yùn)行的狀態(tài),但是并不一定正在執(zhí)行,等待CPU調(diào)度。就緒狀態(tài)的線程可以通過(guò)線程調(diào)度器進(jìn)行調(diào)度,進(jìn)入運(yùn)行狀態(tài)。
- 運(yùn)行狀態(tài)(Running) 線程正在執(zhí)行的狀態(tài),正在占用CPU資源,執(zhí)行任務(wù)。運(yùn)行狀態(tài)的線程可以通過(guò)sleep()、yield()、wait()等方法進(jìn)入阻塞狀態(tài),也可以通過(guò)線程調(diào)度器的調(diào)度進(jìn)入就緒狀態(tài)。
- 阻塞狀態(tài)(Blocked) 線程因?yàn)槟承┰驘o(wú)法執(zhí)行任務(wù),暫時(shí)放棄CPU資源的狀態(tài)。阻塞狀態(tài)包括多種類型:等待阻塞(wait)、同步阻塞(synchronized)、其他阻塞(sleep、join、park等)。當(dāng)阻塞狀態(tài)結(jié)束后,線程可以重新進(jìn)入就緒狀態(tài)等待CPU調(diào)度。
- 死亡狀態(tài)(Dead) 線程執(zhí)行完任務(wù)或者被強(qiáng)制終止,線程生命周期結(jié)束的狀態(tài)。死亡狀態(tài)的線程無(wú)法再次進(jìn)入其他狀態(tài)。
參考文章:??https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.State.html??
2.3 線程的同步與互斥
線程同步是指多個(gè)線程在共享數(shù)據(jù)時(shí)按照一定的規(guī)則進(jìn)行訪問(wèn)和操作,以避免數(shù)據(jù)的混亂和錯(cuò)誤。線程互斥是指多個(gè)線程在對(duì)共享數(shù)據(jù)進(jìn)行訪問(wèn)時(shí),通過(guò)一些機(jī)制防止多個(gè)線程同時(shí)對(duì)數(shù)據(jù)進(jìn)行操作,從而避免數(shù)據(jù)的沖突和錯(cuò)誤。
在Java中,線程同步和互斥是通過(guò)鎖機(jī)制實(shí)現(xiàn)的。鎖是一個(gè)標(biāo)識(shí),用來(lái)保護(hù)共享資源,只有持有鎖的線程才能訪問(wèn)共享資源。Java提供了兩種鎖機(jī)制:synchronized關(guān)鍵字和Lock接口。
synchronized關(guān)鍵字是Java中最基本的鎖機(jī)制,它是一種隱式鎖,只需要在方法或者代碼塊前加上synchronized關(guān)鍵字即可實(shí)現(xiàn)同步和互斥。synchronized關(guān)鍵字的作用是確保同一時(shí)間只有一個(gè)線程可以進(jìn)入同步代碼塊或方法,并且在執(zhí)行完同步代碼塊或方法后會(huì)自動(dòng)釋放鎖。
Lock接口是Java中的另一種鎖機(jī)制,它是一種顯示鎖,需要手動(dòng)加鎖和釋放鎖。Lock接口提供了更加靈活和精細(xì)的鎖控制,比如可以設(shè)置超時(shí)時(shí)間、多個(gè)條件變量等。
線程同步和互斥可以有效避免多個(gè)線程對(duì)共享數(shù)據(jù)的沖突和錯(cuò)誤,保證程序的正確性和穩(wěn)定性。但是如果同步和互斥使用不當(dāng),也會(huì)帶來(lái)一定的性能問(wèn)題,因此需要在使用時(shí)考慮好鎖的粒度、鎖的持有時(shí)間和鎖的競(jìng)爭(zhēng)情況等因素。
2.4 線程的死鎖問(wèn)題
線程死鎖是指在多線程并發(fā)的程序中,兩個(gè)或多個(gè)線程因?yàn)橄嗷サ却龑?duì)方釋放資源而陷入一種無(wú)限等待的狀態(tài),從而導(dǎo)致程序無(wú)法繼續(xù)執(zhí)行的問(wèn)題。簡(jiǎn)單來(lái)說(shuō),就是兩個(gè)或多個(gè)線程互相持有對(duì)方需要的資源,并且都在等待對(duì)方釋放資源,從而導(dǎo)致程序無(wú)法繼續(xù)執(zhí)行。
下面是一個(gè)簡(jiǎn)單的死鎖示例:
public class DeadlockExample {private static Object resource1 = new Object();private static Object resource2 = new Object();public static void main(String[] args) {Thread thread1 = new Thread(() -> {synchronized (resource1) {System.out.println("Thread 1: Holding resource 1...");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1: Waiting for resource 2...");synchronized (resource2) {System.out.println("Thread 1: Holding resource 1 and 2...");}}});Thread thread2 = new Thread(() -> {synchronized (resource2) {System.out.println("Thread 2: Holding resource 2...");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 2: Waiting for resource 1...");synchronized (resource1) {System.out.println("Thread 2: Holding resource 1 and 2...");}}});thread1.start();thread2.start();}
}
在這個(gè)例子中,有兩個(gè)線程分別持有resource1和resource2兩個(gè)資源,并且都在等待對(duì)方釋放資源。如果運(yùn)行這個(gè)程序,就會(huì)陷入死鎖狀態(tài),程序無(wú)法繼續(xù)執(zhí)行。
為了避免死鎖問(wèn)題,我們需要遵循以下原則:
- 避免一個(gè)線程同時(shí)獲取多個(gè)鎖。
- 避免一個(gè)線程在鎖內(nèi)部占用多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源。
- 嘗試使用定時(shí)鎖,使用lock.tryLock(timeout)來(lái)替代使用內(nèi)部鎖機(jī)制。
- 對(duì)于數(shù)據(jù)庫(kù)鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫(kù)事務(wù)中執(zhí)行,否則會(huì)出現(xiàn)死鎖情況。
三. Java中的并發(fā)編程工具類
3.1 synchronized關(guān)鍵字和Lock接口的使用
synchronized關(guān)鍵字
synchronized關(guān)鍵字是Java中最基本的同步機(jī)制,可以用于實(shí)現(xiàn)線程的同步和互斥。synchronized關(guān)鍵字可以應(yīng)用于方法和代碼塊兩種形式。
synchronized方法
synchronized方法可以用于保證同一時(shí)刻只有一個(gè)線程可以訪問(wèn)該方法。當(dāng)一個(gè)線程訪問(wèn)synchronized方法時(shí),其他線程必須等待該線程執(zhí)行完畢后才能訪問(wèn)該方法。
public synchronized void synchronizedMethod() {// do something
}
synchronized代碼塊
synchronized代碼塊可以用于保證同一時(shí)刻只有一個(gè)線程可以訪問(wèn)該代碼塊。synchronized代碼塊需要指定一個(gè)鎖對(duì)象,只有獲取該鎖對(duì)象的線程才能訪問(wèn)該代碼塊。
Object lock = new Object();
public void synchronizedBlock() {synchronized(lock) {// do something}
}
Lock接口
Lock接口是Java中提供的另一種同步機(jī)制,相比于synchronized關(guān)鍵字,Lock接口具有更加靈活的控制能力。Lock接口定義了加鎖和釋放鎖的方法,可以手動(dòng)控制線程的同步和互斥。
加鎖和釋放鎖
Lock接口定義了兩個(gè)核心方法:lock()和unlock()。lock()方法用于加鎖,只有獲取鎖的線程才能進(jìn)入臨界區(qū)執(zhí)行代碼。unlock()方法用于釋放鎖,使得其他線程可以獲取該鎖。
Lock lock = new ReentrantLock();
public void lockMethod() {lock.lock();try {// do something} finally {lock.unlock();}
}
鎖的重入
與synchronized關(guān)鍵字不同,Lock接口可以支持鎖的重入。當(dāng)一個(gè)線程已經(jīng)獲取了鎖,并且在臨界區(qū)內(nèi)嵌套了另一個(gè)加鎖操作時(shí),該線程仍然可以正常執(zhí)行。
Lock lock = new ReentrantLock();
public void reentrantLock() {lock.lock();try {// do somethinglock.lock();try {// do something} finally {lock.unlock();}} finally {lock.unlock();}
}
3.2 原子變量和原子操作類的使用
原子變量
Java提供了一些原子變量類型,例如AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference等,可以直接在多線程環(huán)境下使用,保證操作的原子性。
下面是AtomicInteger的使用方式:
AtomicInteger count = new AtomicInteger(0);
count.getAndIncrement(); // 原子地增加計(jì)數(shù)器
需要注意的是,雖然原子變量能夠保證操作的原子性,但并不能保證線程安全,因此需要考慮其他的同步機(jī)制。
原子操作類
除了原子變量類型之外,Java還提供了一些原子操作類,例如AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray等,可以對(duì)數(shù)組中的元素進(jìn)行原子操作。
下面是AtomicIntegerArray的使用方式:
AtomicIntegerArray array = new AtomicIntegerArray(10);
array.getAndIncrement(0); // 原子地對(duì)數(shù)組元素增加1
需要注意的是,原子操作類的使用方式和原子變量類型類似,同樣需要考慮其他的同步機(jī)制。
總的來(lái)說(shuō),原子變量和原子操作類是Java提供的保證操作原子性的機(jī)制,可以有效避免競(jìng)態(tài)條件問(wèn)題,提高多線程程序的性能和正確性。但需要注意的是,它們并不能完全保證線程安全,仍然需要考慮其他的同步機(jī)制。
3.3 Condition接口和ReentrantLock類的使用
ReentrantLock類是一個(gè)可重入的互斥鎖,它提供了與synchronized關(guān)鍵字類似的功能,但相比synchronized關(guān)鍵字更靈活,能夠?qū)崿F(xiàn)更加復(fù)雜的鎖定操作。同時(shí),ReentrantLock類還提供了一些高級(jí)功能,例如Condition接口,能夠?qū)崿F(xiàn)更加靈活的線程間通信。
ReentrantLock的基本用法
ReentrantLock類的基本用法與synchronized關(guān)鍵字類似,可以用來(lái)實(shí)現(xiàn)互斥鎖,保護(hù)共享資源。
下面是ReentrantLock的基本使用方式:
// 定義一個(gè)ReentrantLock對(duì)象
ReentrantLock lock = new ReentrantLock();// 獲取鎖
lock.lock();
try {// 訪問(wèn)共享資源
} finally {// 釋放鎖lock.unlock();
}
需要注意的是,和synchronized關(guān)鍵字一樣,ReentrantLock也需要在finally塊中釋放鎖,以確保能夠釋放鎖資源。
Condition接口
Condition接口是ReentrantLock的一個(gè)高級(jí)功能,能夠?qū)崿F(xiàn)更加靈活的線程間通信。Condition接口可以用來(lái)實(shí)現(xiàn)等待/通知模式,使得線程能夠更加精確地控制等待和喚醒的條件。
下面是Condition接口的基本使用方式:
// 定義一個(gè)Condition對(duì)象
Condition condition = lock.newCondition();// 等待條件
condition.await();// 喚醒等待條件的線程
condition.signal();
需要注意的是,Condition接口的等待和喚醒操作必須在ReentrantLock的鎖保護(hù)下進(jìn)行,否則會(huì)拋出IllegalMonitorStateException異常。
ReentrantLock和Condition的綜合應(yīng)用
下面是一個(gè)使用ReentrantLock和Condition接口實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式的示例代碼:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class ProducerConsumerExample {private static final int MAX_CAPACITY = 10;private ReentrantLock lock = new ReentrantLock();private Condition notEmpty = lock.newCondition();private Condition notFull = lock.newCondition();private int count = 0;public void produce() throws InterruptedException {lock.lock();try {while (count == MAX_CAPACITY) {notFull.await();}count++;System.out.println("Produced, count = " + count);notEmpty.signal();} finally {lock.unlock();}}public void consume() throws InterruptedException {lock.lock();try {while (count == 0) {notEmpty.await();}count--;System.out.println("Consumed, count = " + count);notFull.signal();} finally {lock.unlock();}}
}
3.4 CountDownLatch類和CyclicBarrier類的使用
CountDownLatch
CountDownLatch是一個(gè)計(jì)數(shù)器,它的作用是允許一個(gè)或多個(gè)線程等待一組事件的完成。CountDownLatch有一個(gè)計(jì)數(shù)器,計(jì)數(shù)器的初始值為一個(gè)正整數(shù),每當(dāng)一個(gè)線程完成了一個(gè)事件,計(jì)數(shù)器的值就減一,當(dāng)計(jì)數(shù)器的值為0時(shí),表示所有事件都已經(jīng)完成,此時(shí)所有等待該事件的線程就可以繼續(xù)執(zhí)行。
使用CountDownLatch的步驟如下:
(1)創(chuàng)建一個(gè)CountDownLatch對(duì)象,并指定計(jì)數(shù)器的初始值。
(2)在等待事件的線程中,調(diào)用CountDownLatch對(duì)象的await()方法進(jìn)行等待,直到計(jì)數(shù)器的值變?yōu)?。
(3)在完成事件的線程中,完成事件后調(diào)用CountDownLatch對(duì)象的countDown()方法,計(jì)數(shù)器的值減一。
示例代碼如下:
import java.util.concurrent.CountDownLatch;public class CountDownLatchTest {public static void main(String[] args) {final CountDownLatch latch = new CountDownLatch(3); // 3個(gè)事件需要等待new Thread(() -> {try {Thread.sleep(1000);System.out.println("事件1完成");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 計(jì)數(shù)器減一}}).start();new Thread(() -> {try {Thread.sleep(2000);System.out.println("事件2完成");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 計(jì)數(shù)器減一}}).start();new Thread(() -> {try {Thread.sleep(3000);System.out.println("事件3完成");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 計(jì)數(shù)器減一}}).start();try {latch.await(); // 等待所有事件完成System.out.println("所有事件已完成");} catch (InterruptedException e) {e.printStackTrace();}}
}
CyclicBarrier
- 創(chuàng)建CyclicBarrier對(duì)象,指定屏障點(diǎn)的數(shù)量和達(dá)到屏障點(diǎn)時(shí)執(zhí)行的任務(wù)。
- 在每個(gè)需要等待屏障點(diǎn)的線程中,調(diào)用await()方法,等待其他線程到達(dá)屏障點(diǎn)。
- 當(dāng)指定數(shù)量的線程都調(diào)用了await()方法后,CyclicBarrier會(huì)執(zhí)行指定的任務(wù),然后釋放所有等待的線程繼續(xù)執(zhí)行。
下面是一個(gè)簡(jiǎn)單的示例,演示如何使用CyclicBarrier:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo {public static void main(String[] args) {int n = 3;CyclicBarrier barrier = new CyclicBarrier(n, () -> {System.out.println("所有線程已到達(dá)屏障點(diǎn),開(kāi)始執(zhí)行任務(wù)!");});for (int i = 0; i < n; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " 到達(dá)屏障點(diǎn)");barrier.await();System.out.println(Thread.currentThread().getName() + " 繼續(xù)執(zhí)行");} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}).start();}}
}
在上面的示例中,我們創(chuàng)建了一個(gè)CyclicBarrier對(duì)象,指定了屏障點(diǎn)的數(shù)量為3,并且指定了所有線程到達(dá)屏障點(diǎn)后要執(zhí)行的任務(wù)。然后我們創(chuàng)建了3個(gè)線程,每個(gè)線程在到達(dá)屏障點(diǎn)后調(diào)用了await()方法等待其他線程到達(dá)屏障點(diǎn)。當(dāng)3個(gè)線程都到達(dá)屏障點(diǎn)后,CyclicBarrier會(huì)執(zhí)行指定的任務(wù),然后釋放所有等待的線程繼續(xù)執(zhí)行。
3.5 Semaphore類和Exchanger類的使用
Semaphore類
Semaphore類是一個(gè)計(jì)數(shù)信號(hào)量,用于控制同時(shí)訪問(wèn)某個(gè)資源的線程數(shù)量。Semaphore維護(hù)了一個(gè)可配置的許可證數(shù)量,線程可以通過(guò)acquire()方法獲取許可證,release()方法釋放許可證。當(dāng)許可證被全部占用時(shí),后續(xù)線程需要等待其他線程釋放許可證后才能獲取到許可證。
Semaphore類的常用方法:
- acquire():獲取一個(gè)許可證,如果沒(méi)有許可證則阻塞等待。
- acquire(int permits):獲取指定數(shù)量的許可證,如果沒(méi)有足夠的許可證則阻塞等待。
- release():釋放一個(gè)許可證。
- release(int permits):釋放指定數(shù)量的許可證。
- availablePermits():獲取當(dāng)前可用的許可證數(shù)量。
Semaphore類的使用示例:
import java.util.concurrent.Semaphore;public class SemaphoreDemo {private static final int THREAD_COUNT = 30;private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);private static Semaphore semaphore = new Semaphore(10); // 設(shè)置最多允許10個(gè)線程同時(shí)訪問(wèn)public static void main(String[] args) {for (int i = 0; i < THREAD_COUNT; i++) {threadPool.execute(() -> {try {semaphore.acquire(); // 獲取許可證System.out.println(Thread.currentThread().getName() + "獲取到許可證,開(kāi)始執(zhí)行任務(wù)");Thread.sleep(5000); // 模擬任務(wù)執(zhí)行時(shí)間System.out.println(Thread.currentThread().getName() + "執(zhí)行任務(wù)完畢,釋放許可證");semaphore.release(); // 釋放許可證} catch (InterruptedException e) {e.printStackTrace();}});}threadPool.shutdown();}
}
Exchanger類
Exchanger是Java中的一個(gè)線程同步工具,它允許兩個(gè)線程在同一個(gè)時(shí)刻交換數(shù)據(jù)。每個(gè)線程通過(guò)調(diào)用exchange()方法來(lái)向?qū)Ψ浇粨Q數(shù)據(jù),當(dāng)兩個(gè)線程都調(diào)用了該方法后,它們會(huì)交換數(shù)據(jù)并繼續(xù)執(zhí)行。
Exchanger的主要方法是exchange()方法,它有兩個(gè)重載的版本:
public V exchange(V x) throws InterruptedException;public V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException;
其中,第一個(gè)方法將指定的數(shù)據(jù)對(duì)象與另一個(gè)線程交換,如果另一個(gè)線程在同一時(shí)刻調(diào)用了exchange()方法,則它的數(shù)據(jù)對(duì)象也會(huì)被返回。如果另一個(gè)線程還沒(méi)有調(diào)用exchange()方法,則當(dāng)前線程會(huì)阻塞等待,直到另一個(gè)線程調(diào)用exchange()方法為止。
第二個(gè)方法與第一個(gè)方法類似,但是增加了一個(gè)超時(shí)參數(shù)。如果在指定的超時(shí)時(shí)間內(nèi)沒(méi)有另一個(gè)線程調(diào)用了exchange()方法,則當(dāng)前線程會(huì)拋出TimeoutException異常。
下面是一個(gè)簡(jiǎn)單的示例程序,展示了如何使用Exchanger來(lái)交換兩個(gè)線程之間的數(shù)據(jù):
import java.util.concurrent.Exchanger;public class ExchangerDemo {public static void main(String[] args) {Exchanger<String> exchanger = new Exchanger<>();new Thread(() -> {String data1 = "Hello";System.out.println("Thread1: send " + data1);try {String data2 = exchanger.exchange(data1);System.out.println("Thread1: received " + data2);} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {String data1 = "World";System.out.println("Thread2: send " + data1);try {String data2 = exchanger.exchange(data1);System.out.println("Thread2: received " + data2);} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}
四. 并發(fā)編程的性能優(yōu)化和注意事項(xiàng)
- 盡量避免使用鎖:鎖是并發(fā)編程中最常見(jiàn)的同步機(jī)制,但它會(huì)引入額外的開(kāi)銷,并且容易導(dǎo)致死鎖和競(jìng)爭(zhēng)條件等問(wèn)題。盡量避免使用鎖,可以使用無(wú)鎖算法、CAS操作、分段鎖等替代方案。
- 減少上下文切換:在多線程環(huán)境下,線程的切換會(huì)引入額外的開(kāi)銷。為了減少上下文切換,可以使用線程池、協(xié)程等技術(shù),避免線程的創(chuàng)建和銷毀,減少線程之間的切換次數(shù)。
- 避免共享變量:共享變量是并發(fā)編程中最常見(jiàn)的競(jìng)爭(zhēng)條件,可能導(dǎo)致數(shù)據(jù)不一致和線程安全等問(wèn)題。盡量避免使用共享變量,可以使用線程局部變量、不可變對(duì)象等替代方案。
- 合理使用并發(fā)容器:Java提供了許多線程安全的容器類,例如ConcurrentHashMap、CopyOnWriteArrayList等。合理使用這些容器類可以避免鎖的競(jìng)爭(zhēng)和死鎖等問(wèn)題。
- 避免死鎖:死鎖是并發(fā)編程中最常見(jiàn)的問(wèn)題之一,容易導(dǎo)致程序的掛起和性能下降。為了避免死鎖,可以使用避免占用多個(gè)鎖、按照相同的順序獲取鎖、設(shè)置超時(shí)等機(jī)制。
- 避免過(guò)度設(shè)計(jì):并發(fā)編程是一種復(fù)雜的編程模型,容易產(chǎn)生過(guò)度設(shè)計(jì)和不必要的優(yōu)化。在編寫并發(fā)程序時(shí),應(yīng)該注意避免過(guò)度設(shè)計(jì),以避免代碼的可讀性和可維護(hù)性降低。