三種方法教你實(shí)現(xiàn)多線程交替打印ABC,干貨滿滿!
1.問題背景
假設(shè)有三個線程,分別打印字母A、B、C。我們需要讓這三個線程交替運(yùn)行,按順序打印出“ABCABCABC...”,直到打印一定次數(shù)或者滿足某個條件。如何通過多線程的協(xié)調(diào)實(shí)現(xiàn)這個任務(wù)呢?這聽起來簡單,實(shí)際涉及到線程之間的同步和互斥,是我們學(xué)習(xí)多線程編程的一個很好的練習(xí)。
2.多線程編程的挑戰(zhàn)
在多線程編程中,最大的問題就是如何控制多個線程的執(zhí)行順序。線程是并發(fā)執(zhí)行的,也就是說它們的執(zhí)行順序在沒有約束的情況下是不可預(yù)知的。為了確保多個線程按照我們期望的順序執(zhí)行,就需要使用一些同步機(jī)制,比如鎖、條件變量、信號量等。
接下來,我會帶大家用三種方式來實(shí)現(xiàn)這個任務(wù)。我們會分別使用Object的wait()和notify()方法、ReentrantLock與Condition、以及信號量來實(shí)現(xiàn)多線程交替打印ABC。
3.方案一:使用wait()和notify()
首先,最常用的一種方法是利用Java中Object類自帶的wait()和notify()方法來實(shí)現(xiàn)線程之間的同步。每個線程在完成它的打印任務(wù)后,通知下一個線程開始執(zhí)行。
實(shí)現(xiàn)步驟
- 定義一個共享對象用來同步。
- 使用wait()讓線程進(jìn)入等待狀態(tài)。
- 使用notify()喚醒下一個線程。
實(shí)現(xiàn)代碼:
圖片
運(yùn)行結(jié)果:
圖片
在這個實(shí)現(xiàn)中,我們使用了wait()和notifyAll()方法來控制線程的執(zhí)行順序。每個線程在不該自己執(zhí)行的時候調(diào)用wait()方法進(jìn)入等待狀態(tài),直到被下一個線程通過notifyAll()方法喚醒。
4.方案二:使用ReentrantLock和Condition
第二種方法是使用ReentrantLock和Condition類。ReentrantLock是Java中更高級的鎖機(jī)制,可以控制多個條件變量,而Condition則可以用來替代wait()和notify()的功能。
實(shí)現(xiàn)步驟
- 定義一個ReentrantLock和多個Condition。
- 每個線程等待相應(yīng)的Condition,當(dāng)符合條件時打印字符并喚醒下一個線程。
實(shí)現(xiàn)代碼
圖片
圖片
運(yùn)行結(jié)果:
圖片
在這個實(shí)現(xiàn)中,我們使用ReentrantLock和Condition來替代了wait()和notify()。通過lock來確保線程安全,通過Condition來控制每個線程的執(zhí)行順序。
5.方案三:使用信號量
最后一種方法是使用Semaphore類。Semaphore是一個計(jì)數(shù)信號量,可以控制多個線程之間的協(xié)調(diào)。這里,我們使用三個信號量,分別控制線程A、B、C的執(zhí)行。
實(shí)現(xiàn)步驟
定義三個信號量semA、semB、semC。
每個線程在自己的信號量上等待,打印完成后釋放下一個線程的信號量。
實(shí)現(xiàn)代碼
圖片
圖片
運(yùn)行結(jié)果:
圖片
在這個實(shí)現(xiàn)中,信號量控制了線程的執(zhí)行順序。初始時,semA的計(jì)數(shù)為1,而semB和semC為0,這保證了線程A先運(yùn)行,然后通過釋放信號量來依次喚醒線程B和C。
今天我們學(xué)習(xí)了三種實(shí)現(xiàn)多線程交替打印ABC的方法:使用wait()和notify(),使用ReentrantLock和Condition,以及使用信號量。通過這些方法,我們可以有效地控制線程之間的同步與互斥,解決復(fù)雜的并發(fā)問題。
這三種方法各有優(yōu)劣。wait()和notify()較為基礎(chǔ),適合簡單的場景;ReentrantLock和Condition提供了更細(xì)粒度的控制,適合復(fù)雜的并發(fā)場景;而Semaphore則是一種經(jīng)典的計(jì)數(shù)信號量機(jī)制,在某些場景下顯得更加直觀和高效。