1. 前序
在多線程編程中长已,線程同步是確保數(shù)據(jù)一致性和防止競(jìng)態(tài)條件的關(guān)鍵资锰。Java 提供了多種用于線程同步的機(jī)制髓梅,以解決不同場(chǎng)景下的線程競(jìng)爭(zhēng)問(wèn)題。無(wú)論是最基本的 synchronized
關(guān)鍵字便瑟,還是更靈活的 ReentrantLock
、ReentrantReadWriteLock
憨颠,它們都為開(kāi)發(fā)者提供了不同級(jí)別的鎖和控制胳徽。
本文將逐一介紹 Java 中常見(jiàn)的同步機(jī)制,涵蓋了 synchronized
爽彤、ReentrantLock
养盗、Atomic
類等,同時(shí)給出每種機(jī)制的示例代碼和適用場(chǎng)景适篙,幫助你更好地理解并應(yīng)用這些同步機(jī)制往核。
接下來(lái),我們將詳細(xì)討論并演示每種方式的使用方法嚷节。
2. synchronized
2.1 synchronized
關(guān)鍵字
-
描述:Java 中最基礎(chǔ)的同步機(jī)制就是
synchronized
關(guān)鍵字聂儒,它可以用于方法或代碼塊,確保同一時(shí)刻只有一個(gè)線程可以訪問(wèn)共享資源硫痰。 - 示例代碼:
/**
* 示例類衩婚,演示如何使用 synchronized 方法進(jìn)行線程同步
*/
public class SynchronizedMethodExample {
/** 共享計(jì)數(shù)器 */
private int counter = 0;
/**
* 同步遞增計(jì)數(shù)器的方法,確保同一時(shí)刻只有一個(gè)線程可以執(zhí)行
*/
public synchronized void increment() {
// 遞增計(jì)數(shù)器
counter++;
// 輸出當(dāng)前線程和計(jì)數(shù)器的值
System.out.println(Thread.currentThread().getName() + " - Counter: " + counter);
}
/**
* 主程序入口效斑,創(chuàng)建多個(gè)線程并運(yùn)行
* @param args 默認(rèn)參數(shù)
*/
public static void main(String[] args) {
SynchronizedMethodExample example = new SynchronizedMethodExample();
// 線程任務(wù)非春,調(diào)用 increment 方法
Runnable task = example::increment;
// 創(chuàng)建兩個(gè)線程
Thread t1 = new Thread(task, "Thread 1");
Thread t2 = new Thread(task, "Thread 2");
// 啟動(dòng)線程
t1.start();
t2.start();
}
}
2.2 使用 synchronized
代碼塊
-
描述:相比于
synchronized
方法,synchronized
代碼塊允許更細(xì)粒度地控制同步范圍缓屠∑骊迹可以指定特定的代碼塊進(jìn)行同步,而不是整個(gè)方法敌完,這樣可以減少鎖的競(jìng)爭(zhēng)储耐,提高效率。 - 示例代碼:
/**
* 示例類滨溉,演示如何使用 synchronized 代碼塊進(jìn)行線程同步
*/
public class SynchronizedBlockExample {
/** 共享計(jì)數(shù)器 */
private int counter = 0;
/** 自定義鎖對(duì)象 */
private final Object lock = new Object();
/**
* 同步遞增計(jì)數(shù)器的方法什湘,只鎖定代碼塊
*/
public void increment() {
// 使用 synchronized 代碼塊確保鎖定的粒度更小
synchronized (lock) {
counter++;
System.out.println(Thread.currentThread().getName() + " - Counter: " + counter);
}
}
/**
* 主程序入口长赞,創(chuàng)建多個(gè)線程并運(yùn)行
* @param args 默認(rèn)參數(shù)
*/
public static void main(String[] args) {
SynchronizedBlockExample example = new SynchronizedBlockExample();
Runnable task = example::increment;
Thread t1 = new Thread(task, "Thread 1");
Thread t2 = new Thread(task, "Thread 2");
t1.start();
t2.start();
}
}
3. ReentrantLock
-
描述:
ReentrantLock
是Lock
接口的一個(gè)常用實(shí)現(xiàn),它提供了更靈活的鎖定機(jī)制闽撤,允許手動(dòng)加鎖和解鎖涧卵。 - 示例代碼:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 示例類,演示如何使用 ReentrantLock 進(jìn)行線程同步
*/
public class LockExample {
/** 共享計(jì)數(shù)器 */
private int counter = 0;
/** ReentrantLock 實(shí)例 */
private final Lock lock = new ReentrantLock();
/**
* 同步遞增計(jì)數(shù)器的方法腹尖,手動(dòng)加鎖和解鎖
*/
public void increment() {
// 獲取鎖
lock.lock();
try {
// 遞增計(jì)數(shù)器
counter++;
// 輸出當(dāng)前線程和計(jì)數(shù)器的值
System.out.println(Thread.currentThread().getName() + " - Counter: " + counter);
} finally {
// 確保鎖在最后被釋放
lock.unlock();
}
}
/**
* 主程序入口柳恐,創(chuàng)建多個(gè)線程并運(yùn)行
* @param args 默認(rèn)參數(shù)
*/
public static void main(String[] args) {
LockExample example = new LockExample();
Runnable task = example::increment;
Thread t1 = new Thread(task, "Thread 1");
Thread t2 = new Thread(task, "Thread 2");
t1.start();
t2.start();
}
}
4. ReentrantReadWriteLock
-
描述:
ReentrantReadWriteLock
提供了讀寫鎖機(jī)制,可以讓多個(gè)線程并發(fā)讀取热幔,但在寫入時(shí)只有一個(gè)線程可以操作乐设。這樣可以提高在讀多寫少場(chǎng)景下的性能。 - 示例代碼:
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 示例類绎巨,演示如何使用 ReentrantReadWriteLock 進(jìn)行線程同步
*/
public class ReadWriteLockExample {
/** 共享計(jì)數(shù)器 */
private int counter = 0;
/** ReentrantReadWriteLock 實(shí)例 */
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**
* 獲取寫鎖并遞增計(jì)數(shù)器
*/
public void increment() {
// 獲取寫鎖
lock.writeLock().lock();
try {
// 遞增計(jì)數(shù)器
counter++;
System.out.println(Thread.currentThread().getName() + " - Write Counter: " + counter);
} finally {
// 釋放寫鎖
lock.writeLock().unlock();
}
}
/**
* 獲取讀鎖并讀取計(jì)數(shù)器
* @return 計(jì)數(shù)器值
*/
public int getCounter() {
// 獲取讀鎖
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " - Read Counter: " + counter);
return counter;
} finally {
// 釋放讀鎖
lock.readLock().unlock();
}
}
/**
* 主程序入口近尚,創(chuàng)建多個(gè)線程并運(yùn)行
* @param args 默認(rèn)參數(shù)
*/
public static void main(String[] args) {
ReadWriteLockExample example = new ReadWriteLockExample();
Runnable writeTask = example::increment;
Runnable readTask = example::getCounter;
Thread t1 = new Thread(writeTask, "Writer Thread");
Thread t2 = new Thread(readTask, "Reader Thread");
t1.start();
t2.start();
}
}
5. Atomic
類
-
描述:
Atomic
類位于java.util.concurrent.atomic
包內(nèi),提供了常見(jiàn)的原子操作類(如AtomicInteger
)场勤,用于在無(wú)鎖的情況下對(duì)單一變量進(jìn)行線程安全的操作戈锻。 - 示例代碼:
import java.util.concurrent.atomic.AtomicInteger;
/**
* 示例類,演示如何使用 AtomicInteger 進(jìn)行線程同步
*/
public class AtomicExample {
/** 線程安全的 AtomicInteger */
private AtomicInteger counter = new AtomicInteger(0);
/**
* 原子性遞增計(jì)數(shù)器的方法
*/
public void increment() {
// 原子遞增
int newValue = counter.incrementAndGet();
System.out.println(Thread.currentThread().getName() + " - Counter: " + newValue);
}
/**
* 主程序入口和媳,創(chuàng)建多個(gè)線程并運(yùn)行
* @param args 默認(rèn)參數(shù)
*/
public static void main(String[] args) {
AtomicExample example = new AtomicExample();
Runnable task = example::increment;
Thread t1 = new Thread(task, "Thread 1");
Thread t2 = new Thread(task, "Thread 2");
t1.start();
t2.start();
}
}
6. CyclicBarrier
-
描述:
CyclicBarrier
是一種允許一組線程相互等待的同步機(jī)制格遭,直到所有線程都到達(dá)某個(gè)共同的屏障點(diǎn)時(shí),才能繼續(xù)執(zhí)行留瞳。它支持重用拒迅,即可以被多次使用。 - 示例代碼:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* 示例類她倘,演示如何使用 CyclicBarrier 實(shí)現(xiàn)線程同步
*/
public class CyclicBarrierExample {
/** CyclicBarrier 實(shí)例璧微,等待 3 個(gè)線程 */
private final CyclicBarrier barrier = new CyclicBarrier(3, () -> {
// 所有線程到達(dá)屏障后執(zhí)行的操作
System.out.println("All threads have reached the barrier. Barrier action executed.");
});
/**
* 線程任務(wù),等待屏障點(diǎn)并繼續(xù)執(zhí)行
*/
public void performTask() {
System.out.println(Thread.currentThread().getName() + " is waiting at the barrier");
try {
// 等待其他線程到達(dá)屏障點(diǎn)
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " has crossed the barrier");
}
/**
* 主程序入口硬梁,創(chuàng)建多個(gè)線程并運(yùn)行
* @param args 默認(rèn)參數(shù)
*/
public static void main(String[] args) {
CyclicBarrierExample example = new CyclicBarrierExample();
Thread t1 = new Thread(example::performTask, "Thread 1");
Thread t2 = new Thread(example::performTask, "Thread 2");
Thread t3 = new Thread(example::performTask, "Thread 3");
t1.start();
t2.start();
t3.start();
}
}
7. Object
的 wait()
和 notify()
方法
-
描述:
Object
類的wait()
前硫、notify()
和notifyAll()
方法允許線程在某個(gè)條件下進(jìn)行等待和喚醒。與synchronized
搭配使用荧止,可以實(shí)現(xiàn)類似于信號(hào)量的功能屹电。 - 示例代碼:
/**
* 示例類,演示如何使用 wait() 和 notify() 進(jìn)行線程同步
*/
public class WaitNotifyExample {
/** 自定義鎖對(duì)象 */
private final Object lock = new Object();
/** 標(biāo)志位罩息,表示是否已經(jīng)生產(chǎn)了數(shù)據(jù) */
private boolean isProduced = false;
/**
* 生產(chǎn)者方法嗤详,等待消費(fèi)者消費(fèi)后生產(chǎn)新數(shù)據(jù)
* @throws InterruptedException 當(dāng)線程被中斷時(shí)拋出異常
*/
public void produce() throws InterruptedException {
synchronized (lock) {
// 如果已經(jīng)生產(chǎn)了數(shù)據(jù)个扰,等待消費(fèi)者消費(fèi)
while (isProduced) {
lock.wait();
}
// 生產(chǎn)數(shù)據(jù)
System.out.println(Thread.currentThread().getName() + " produced data.");
isProduced = true;
// 通知消費(fèi)者可以消費(fèi)數(shù)據(jù)了
lock.notify();
}
}
/**
* 消費(fèi)者方法瓷炮,等待生產(chǎn)者生產(chǎn)數(shù)據(jù)并進(jìn)行消費(fèi)
* @throws InterruptedException 當(dāng)線程被中斷時(shí)拋出異常
*/
public void consume() throws InterruptedException {
synchronized (lock) {
// 如果還沒(méi)有生產(chǎn)數(shù)據(jù),等待生產(chǎn)者生產(chǎn)
while (!isProduced) {
lock.wait();
}
// 消費(fèi)數(shù)據(jù)
System.out.println(Thread.currentThread().getName() + " consumed data.");
isProduced = false;
// 通知生產(chǎn)者可以繼續(xù)生產(chǎn)數(shù)據(jù)了
lock.notify();
}
}
/**
* 主程序入口递宅,創(chuàng)建生產(chǎn)者和消費(fèi)者線程并運(yùn)行
* @param args 默認(rèn)參數(shù)
*/
public static void main(String[] args) {
WaitNotifyExample example = new WaitNotifyExample();
// 創(chuàng)建生產(chǎn)者線程
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
example.produce();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Producer");
// 創(chuàng)建消費(fèi)者線程
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
example.consume();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Consumer");
producer.start();
consumer.start();
}
}
8. 總結(jié)
Java 提供了多種用于線程同步的機(jī)制娘香,包括 synchronized
苍狰、ReentrantLock
、ReentrantReadWriteLock
烘绽、Atomic
類淋昭、CyclicBarrier
以及 Object
的 wait()
/notify()
。每種方式都有其適用場(chǎng)景和優(yōu)缺點(diǎn)安接。對(duì)于簡(jiǎn)單的同步需求翔忽,synchronized
是一種直接而有效的選擇;對(duì)于復(fù)雜的并發(fā)控制盏檐,Lock
提供了更靈活的鎖機(jī)制歇式;而 wait()
和 notify()
可以實(shí)現(xiàn)線程之間的協(xié)調(diào)工作。