Java 中使用同步線程的多種實(shí)現(xiàn)方式
1. 前序
在多線程編程中,線程同步是確保數(shù)據(jù)一致性和防止競態(tài)條件的關(guān)鍵。Java 提供了多種用于線程同步的機(jī)制,以解決不同場景下的線程競爭問題。無論是最基本的 synchronized? 關(guān)鍵字,還是更靈活的 ReentrantLock?、ReentrantReadWriteLock?,它們都為開發(fā)者提供了不同級(jí)別的鎖和控制。
本文將逐一介紹 Java 中常見的同步機(jī)制,涵蓋了 synchronized?、ReentrantLock?、Atomic? 類等,同時(shí)給出每種機(jī)制的示例代碼和適用場景,幫助你更好地理解并應(yīng)用這些同步機(jī)制。
2.synchronized?
2.1synchronized? 關(guān)鍵字
- 描述:Java 中最基礎(chǔ)的同步機(jī)制就是
synchronized? 關(guān)鍵字,它可以用于方法或代碼塊,確保同一時(shí)刻只有一個(gè)線程可以訪問共享資源。 - 示例代碼:
/**
* 示例類,演示如何使用 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è)方法,這樣可以減少鎖的競爭,提高效率。 - 示例代碼:
/**
* 示例類,演示如何使用 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è)線程可以操作。這樣可以提高在讀多寫少場景下的性能。 - 示例代碼:
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),提供了常見的原子操作類(如AtomicInteger?),用于在無鎖的情況下對(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) {
// 如果還沒有生產(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()?。每種方式都有其適用場景和優(yōu)缺點(diǎn)。對(duì)于簡單的同步需求,synchronized? 是一種直接而有效的選擇;對(duì)于復(fù)雜的并發(fā)控制,Lock? 提供了更靈活的鎖機(jī)制;而 wait()? 和 notify()? 可以實(shí)現(xiàn)線程之間的協(xié)調(diào)工作。
到此這篇關(guān)于Java 中使用同步線程的多種實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)Java 同步線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring使用aop切面編程時(shí)要給那些類加注解的實(shí)例
在使用切面編程時(shí),通常需要為以下類或組件添加注解來標(biāo)識(shí)它們,以便 Spring 或其他切面框架能夠正確識(shí)別和處理它們,這篇文章主要介紹了Spring使用aop切面編程時(shí)要給那些類加注解,需要的朋友可以參考下2023-11-11
SpringBoot多數(shù)據(jù)源讀寫分離的自定義配置問題及解決方法
這篇文章主要介紹了SpringBoot多數(shù)據(jù)源讀寫分離的自定義配置,我們可以通過自定義配置數(shù)據(jù)庫配置類來解決這個(gè)問題,方式有很多,不同的業(yè)務(wù)采用的方式也不同,下面我簡單的介紹我們項(xiàng)目的使用的方法2022-06-06
JSON.parseObject和JSON.toJSONString實(shí)例詳解
這篇文章主要為大家詳細(xì)介紹了JSON.parseObject和JSON.toJSONString實(shí)例,具有一定的參考價(jià)值,感興趣的朋友可以參考一下2018-06-06
Spring Cloud 的 Hystrix.功能及實(shí)踐詳解
這篇文章主要介紹了Spring Cloud 的 Hystrix.功能及實(shí)踐詳解,Hystrix 具備服務(wù)降級(jí)、服務(wù)熔斷、線程和信號(hào)隔離、請(qǐng)求緩存、請(qǐng)求合并以及服務(wù)監(jiān)控等強(qiáng)大功能,需要的朋友可以參考下2019-07-07
G1垃圾回收器在并發(fā)場景調(diào)優(yōu)詳解
這篇文章主要為大家介紹了G1垃圾回收器在并發(fā)場景調(diào)優(yōu)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04

