Java鎖機制詳細講解與實戰(zhàn)應用
引言
在多線程編程中,確保數(shù)據(jù)的并發(fā)訪問安全是至關重要的。Java作為支持多線程編程的主流語言,提供了多種內(nèi)置和高級鎖機制來解決共享資源的競爭問題,從而保證線程間的同步與協(xié)作。本文將全面探討Java中的鎖機制,從基本概念、類型到具體實現(xiàn)方法,并結合實際應用場景進行說明。
一、synchronized關鍵字
Java中的synchronized關鍵字是一種內(nèi)置鎖機制,用于保證多線程環(huán)境下的線程安全。它提供了簡單易用的互斥訪問控制,確保同一時刻只有一個線程可以執(zhí)行特定代碼塊。
下面是一個使用synchronized關鍵字的示例代碼:
public class SynchronizedExample {
private int counter;
public synchronized void increment() {
counter++;
}
public synchronized int getCount() {
return counter;
}
}在上面的示例中,increment() 和 getCount() 方法都使用了 synchronized 關鍵字。這意味著當一個線程正在執(zhí)行其中一個方法時,其他線程想要執(zhí)行另一個方法將被阻塞,直到當前線程執(zhí)行完畢并釋放鎖。
synchronized 關鍵字可以用于以下場景:
- 修飾實例方法:如示例中的 increment() 和 getCount() 方法。當一個線程訪問某個對象的同步方法時,其他線程對該對象的其他同步方法訪問將被阻塞。
- 修飾靜態(tài)方法:可以使用 synchronized 關鍵字修飾靜態(tài)方法,以實現(xiàn)對整個類的同步訪問。
public class SynchronizedStaticExample {
private static int counter;
public static synchronized void increment() {
counter++;
}
public static synchronized int getCount() {
return counter;
}
}- 修飾代碼塊:可以使用 synchronized 關鍵字修飾代碼塊,以實現(xiàn)對特定資源的同步訪問。
public class SynchronizedBlockExample {
private int counter;
private Object lock = new Object();
public void increment() {
synchronized(lock) {
counter++;
}
}
public int getCount() {
synchronized(lock) {
return counter;
}
}
}在上面的示例中,increment() 和 getCount() 方法內(nèi)部的同步代碼塊使用了同一個鎖對象 lock。這意味著當一個線程訪問其中一個同步代碼塊時,其他線程想要訪問另一個同步代碼塊將被阻塞,直到當前線程執(zhí)行完畢并釋放鎖。
二、內(nèi)置鎖(Intrinsic Locks or Monitor Locks)
Java內(nèi)置鎖(Intrinsic Locks 或 Monitor Locks)是基于JVM實現(xiàn)的一種同步機制,每個Java對象都可以關聯(lián)一個內(nèi)置鎖。當線程試圖訪問被 synchronized 關鍵字修飾的方法或代碼塊時,會嘗試獲取該對象的內(nèi)置鎖,如果成功,則可以執(zhí)行相應的臨界區(qū)代碼;如果失?。存i已被其他線程持有),則當前線程將進入阻塞狀態(tài),等待鎖釋放。
以下是一個使用Java內(nèi)置鎖的示例代碼:
public class IntrinsicLockExample {
private int counter = 0;
// 同步實例方法,隱式使用 this 對象作為鎖
public synchronized void increment() {
counter++;
}
// 同步代碼塊,顯式指定鎖對象
public void incrementWithBlock(Object lock) {
synchronized (lock) {
counter++;
}
}
// 獲取當前計數(shù)
public synchronized int getCount() {
return counter;
}
// 示例類中的靜態(tài)變量和對應的同步方法
private static int staticCounter = 0;
public static synchronized void incrementStatic() {
staticCounter++;
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
IntrinsicLockExample example = new IntrinsicLockExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + example.getCount()); // 輸出結果應為2000
// 靜態(tài)成員的同步示例
for (int i = 0; i < 500; i++) {
IntrinsicLockExample.incrementStatic();
}
System.out.println("Final static count: " + IntrinsicLockExample.staticCounter); // 輸出結果應為500
}
}詳細解釋:
- 在 increment() 方法中,使用了 synchronized 關鍵字修飾,這意味著每次只有一個線程可以執(zhí)行這個方法,確保了對 counter 變量的并發(fā)訪問安全。
- incrementWithBlock() 方法展示了如何在代碼塊級別使用內(nèi)置鎖,通過傳入一個 Object 類型的鎖參數(shù),多個方法可以共享同一把鎖,達到同步的目的。
- getCount() 方法同樣使用了 synchronized,保證了讀取 counter 的線程安全。
- incrementStatic() 是一個同步靜態(tài)方法,它使用的是類級別的內(nèi)置鎖,因此在同一時刻只能有一個線程修改 staticCounter 變量。
三、顯示鎖(Lock)
Java顯示鎖(顯示鎖通常指的是java.util.concurrent.locks.Lock接口及其實現(xiàn)類)提供了比內(nèi)置鎖(synchronized關鍵字)更強大和靈活的線程同步機制。顯示鎖允許程序員更加精確地控制線程的加鎖和解鎖行為,支持中斷請求,以及非阻塞式的嘗試獲取鎖等特性。
以下是使用java.util.concurrent.locks.ReentrantLock作為顯示鎖的一個示例代碼:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DisplayedLockExample {
private final Lock lock = new ReentrantLock();
private int counter;
public void increment() {
lock.lock(); // 加鎖
try {
counter++;
} finally {
lock.unlock(); // 無論何時都要確保解鎖
}
}
public int getCount() {
lock.lock(); // 同樣需要加鎖保護
try {
return counter;
} finally {
lock.unlock();
}
}
// 顯示鎖還提供了更多的控制方式,比如嘗試獲取鎖,支持中斷等
public void tryIncrementWithTimeout(int timeout, TimeUnit unit) throws InterruptedException {
if (lock.tryLock(timeout, unit)) {
try {
counter++;
} finally {
lock.unlock();
}
} else {
System.out.println("未能在規(guī)定時間內(nèi)獲取到鎖");
}
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
DisplayedLockExample example = new DisplayedLockExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + example.getCount()); // 輸出結果應為2000
}
}詳細解釋:
- DisplayedLockExample 類中定義了一個 ReentrantLock 對象作為顯示鎖。
- increment() 和 getCount() 方法在修改或讀取 counter 變量時,均首先獲取鎖,然后在 finally 語句塊中確保無論如何都能釋放鎖,這是一種最佳實踐,確保即使在異常情況下也能正確釋放鎖。
- tryIncrementWithTimeout() 方法演示了顯示鎖的超時獲取功能,它嘗試在指定時間內(nèi)獲取鎖,若超過設定時間仍無法獲取,則不再等待并繼續(xù)執(zhí)行后續(xù)邏輯。
顯示鎖相比內(nèi)置鎖的優(yōu)勢:
- 可以嘗試非阻塞地獲取鎖,比如 tryLock() 和 tryLock(long timeout, TimeUnit unit) 方法。
- 支持中斷,線程在等待鎖時可以響應中斷請求,這對于那些需要取消長時間等待的任務非常有用。
- 可以實現(xiàn)公平鎖策略,即按照線程請求鎖的順序來分配鎖,避免“饑餓”現(xiàn)象。
- 提供了鎖的監(jiān)聽和喚醒機制,可通過 Condition 對象實現(xiàn)更復雜的同步結構
四、讀寫鎖(Read-Write Locks)
Java的讀寫鎖(Read-Write Locks)是一種特殊的鎖機制,它允許多個讀取者同時訪問共享資源,但在寫入者訪問時會排斥所有讀取者和其他寫入者。這使得在讀多寫少的情況下,系統(tǒng)的并發(fā)性能得到顯著提升。Java中實現(xiàn)讀寫鎖的主要類是java.util.concurrent.locks.ReadWriteLock,以及它的標準實現(xiàn)java.util.concurrent.locks.ReentrantReadWriteLock。
以下是一個使用ReentrantReadWriteLock的示例代碼及其詳細解釋:
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private int sharedResource;
public void read() {
readLock.lock(); // 獲取讀鎖
try {
// 多個線程可以同時在這里讀取數(shù)據(jù)
System.out.println("Reading the resource: " + sharedResource);
} finally {
readLock.unlock(); // 釋放讀鎖
}
}
public void update() {
writeLock.lock(); // 獲取寫鎖
try {
// 當有線程在執(zhí)行這里的寫操作時,其他所有讀寫線程都會被阻塞
sharedResource++;
System.out.println("Updated the resource to: " + sharedResource);
} finally {
writeLock.unlock(); // 釋放寫鎖
}
}
// 使用示例
public static void main(String[] args) {
ReadWriteLockExample example = new ReadWriteLockExample();
ExecutorService executor = Executors.newFixedThreadPool(10);
// 創(chuàng)建大量讀取任務
for (int i = 0; i < 20; i++) {
executor.submit(() -> example.read());
}
// 創(chuàng)建少量寫入任務
for (int i = 0; i < 5; i++) {
executor.submit(() -> example.update());
}
// 關閉線程池
executor.shutdown();
}
}詳細解釋:
- ReadWriteLockExample 類中定義了一個 ReentrantReadWriteLock 對象,并從中提取出讀鎖 readLock 和寫鎖 writeLock。
- read() 方法獲取讀鎖后讀取共享資源 sharedResource,此時如果有多個線程同時調(diào)用 read() 方法,它們可以同時執(zhí)行,因為讀鎖是共享的。
- update() 方法獲取寫鎖后更新 sharedResource,在執(zhí)行寫操作時,所有其他嘗試獲取讀鎖或?qū)戞i的線程都會被阻塞,直到寫操作完成并釋放寫鎖為止。
- 在主方法中,我們創(chuàng)建了一個線程池,提交了大量的讀任務和少量的寫任務,模擬了讀多寫少的場景,這時讀寫鎖可以有效提高系統(tǒng)并發(fā)性能。
五、條件變量(Condition Objects)
在Java中,條件變量是通過java.util.concurrent.locks.Condition接口實現(xiàn)的,它與鎖(如ReentrantLock)一起使用,允許線程等待滿足特定條件時被喚醒。下面是一個使用Condition對象的示例代碼及詳細解釋:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionVariableExample {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
// 共享資源,模擬隊列為空的情況
private boolean isEmpty = true;
public void produce() {
lock.lock();
try {
// 當隊列為空時,生產(chǎn)者線程等待condition被signal
while (!isEmpty) {
condition.await(); // 線程在此處釋放鎖并進入等待狀態(tài)
}
// 生產(chǎn)商品邏輯...
System.out.println("Produced an item, queue is no longer empty.");
isEmpty = false; // 更新條件
// 喚醒所有等待此condition的消費者線程
condition.signalAll();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock(); // 不論如何都要確保解鎖
}
}
public void consume() {
lock.lock();
try {
// 當隊列非空時,消費者線程等待condition被signal
while (isEmpty) {
condition.await(); // 線程在此處釋放鎖并進入等待狀態(tài)
}
// 消費商品邏輯...
System.out.println("Consumed an item, queue is now empty.");
isEmpty = true; // 更新條件
// 喚醒所有等待此condition的生產(chǎn)者線程
condition.signalAll();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock(); // 不論如何都要確保解鎖
}
}
// 使用示例
public static void main(String[] args) throws InterruptedException {
ConditionVariableExample example = new ConditionVariableExample();
Thread producer = new Thread(example::produce);
Thread consumer = new Thread(example::consume);
producer.start();
consumer.start();
// 確保生產(chǎn)者先啟動并改變條件
Thread.sleep(100);
example.produce(); // 手動調(diào)用一次生產(chǎn)方法,以初始化條件變化
producer.join();
consumer.join();
}
}詳細解釋:
- ConditionVariableExample 類中定義了一個 ReentrantLock 對象和從該鎖對象創(chuàng)建的一個 Condition 對象。
- 在 produce() 方法中,當共享資源(這里用 isEmpty 標記隊列是否為空)為真(即隊列為空)時,生產(chǎn)者線程調(diào)用 condition.await() 方法進入等待狀態(tài),并釋放鎖。這樣,其他線程可以修改 isEmpty 的值。
- 當 consume() 方法中的消費者線程檢測到 isEmpty 為假(即隊列非空),則消費者線程調(diào)用 condition.await() 進入等待狀態(tài)。
- 當條件發(fā)生變化時,比如生產(chǎn)者生產(chǎn)了物品使得隊列不再為空,則會調(diào)用 condition.signalAll() 來喚醒所有等待該條件的線程。
- 注意,每個方法都在 try-finally 結構中管理鎖的加鎖和解鎖操作,確保即使在異常情況下也能正確釋放鎖。
六、樂觀鎖(Optimistic Locking)
樂觀鎖在Java中的實現(xiàn)通常依賴于原子變量類(如java.util.concurrent.atomic包下的類)或數(shù)據(jù)庫事務中的版本號機制。樂觀鎖的假設是大多數(shù)情況下數(shù)據(jù)不會發(fā)生沖突,因此在修改數(shù)據(jù)前并不加鎖,而是在更新時檢查在此期間是否有其他線程修改過該數(shù)據(jù)。如果發(fā)現(xiàn)數(shù)據(jù)未被修改,則執(zhí)行更新操作;否則則需要重新讀取、驗證并嘗試更新。
以下是一個使用AtomicInteger作為樂觀鎖機制實現(xiàn)的簡單示例:
import java.util.concurrent.atomic.AtomicInteger;
public class OptimisticLockingExample {
private AtomicInteger counter = new AtomicInteger(0);
public void increment() {
// 讀取當前值
int currentValue;
do {
// 獲取一個可能過時的值
currentValue = counter.get();
// 檢查在此期間是否已經(jīng)被其他線程修改過
} while (!counter.compareAndSet(currentValue, currentValue + 1)); // 如果當前值未變,則更新為原值+1
System.out.println("Counter incremented to: " + counter.get());
}
public static void main(String[] args) {
OptimisticLockingExample example = new OptimisticLockingExample();
Thread thread1 = new Thread(example::increment);
Thread thread2 = new Thread(example::increment);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}詳細解釋:
- 在這個例子中,我們使用了AtomicInteger來模擬一個計數(shù)器,它具有原子性的get()和compareAndSet()方法。
- compareAndSet()方法(也稱為CAS操作)嘗試將當前值與預期值進行比較,如果當前值等于預期值,則以原子方式更新為新值。這里的預期值就是我們在循環(huán)開始時獲取到的currentValue。
- 當多個線程同時調(diào)用increment()方法時,它們都會嘗試更新計數(shù)器。如果在某一線程嘗試更新之前,計數(shù)器已被其他線程更新,則其compareAndSet()會失敗,并且該線程將繼續(xù)下一次循環(huán),再次獲取最新的currentValue并嘗試更新。
- 這樣,在并發(fā)環(huán)境下,樂觀鎖通過不斷的重試確保了最終只有一個線程成功地進行了原子性更新,從而實現(xiàn)了線程安全的計數(shù)操作。
七、悲觀鎖(Pessimistic Locking)
悲觀鎖在Java中通常表現(xiàn)為獲取到一個鎖后,其他線程嘗試訪問該資源時會立即被阻塞,直到持有鎖的線程釋放鎖。最直接的例子就是使用synchronized關鍵字修飾的方法或代碼塊。但為了更好地說明數(shù)據(jù)庫層面的悲觀鎖實現(xiàn),這里提供一個基于JDBC和Hibernate的示例:
JDBC示例(通過SQL的SELECT ... FOR UPDATE實現(xiàn)悲觀鎖):
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class PessimisticLockingExampleJDBC {
private static final String URL = "jdbc:mysql://localhost:3306/mydb";
private static final String USER = "root";
private static final String PASSWORD = "password";
public void updateDataWithPessimisticLock(int id) {
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
connection.setAutoCommit(false); // 關閉自動提交,開始事務
// 使用FOR UPDATE來獲取悲觀鎖
String sql = "SELECT * FROM my_table WHERE id = ? FOR UPDATE";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, id);
preparedStatement.execute();
// 假設我們從查詢結果中獲取了數(shù)據(jù),并準備更新它...
// 更新操作
String updateSql = "UPDATE my_table SET column = ? WHERE id = ?";
preparedStatement = connection.prepareStatement(updateSql);
preparedStatement.setString(1, "new_value");
preparedStatement.setInt(2, id);
preparedStatement.executeUpdate();
connection.commit(); // 提交事務,釋放鎖
} catch (SQLException e) {
// 處理異常并回滾事務
try {
if (connection != null) {
connection.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
}Hibernate示例(通過Session的鎖定方法實現(xiàn)悲觀鎖):
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class PessimisticLockingExampleHibernate {
private SessionFactory sessionFactory;
public PessimisticLockingExampleHibernate() {
Configuration configuration = new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
}
public void updateDataWithPessimisticLock(Integer id) {
try (Session session = sessionFactory.openSession()) {
// 開始事務
session.beginTransaction();
// 加載實體并顯式地請求悲觀鎖
MyEntity entity = session.get(MyEntity.class, id, LockMode.PESSIMISTIC_WRITE);
// 假設我們在這里修改了entity的屬性值...
entity.setProperty("new_value");
// 提交事務,同時釋放鎖
session.getTransaction().commit();
} catch (Exception e) {
// 如果發(fā)生異常,則回滾事務
if (session != null && session.getTransaction() != null && session.getTransaction().isActive()) {
session.getTransaction().rollback();
}
e.printStackTrace();
}
}
}
@Entity
@Table(name = "my_table")
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// 其他屬性及getter、setter省略...
}詳細解釋:
- 在JDBC示例中,通過設置事務并執(zhí)行SELECT ... FOR UPDATE語句,在讀取記錄的同時獲得了對該記錄的悲觀鎖,使得其他事務無法修改這條記錄,直至當前事務結束。
- 在Hibernate示例中,通過session.get()方法加載實體對象時指定了LockMode.PESSIMISTIC_WRITE模式,這樣在獲取實體時即獲得了一把悲觀寫鎖,阻止其他事務對同一實體進行并發(fā)修改。
八、自旋鎖(Spin Locks)
自旋鎖在Java中主要用于解決線程間短時間的同步問題,尤其適用于等待時間極短并且CPU資源相對充足的場景。在Java中并沒有直接提供名為“自旋鎖”的API,但是可以通過循環(huán)和volatile關鍵字模擬實現(xiàn)自旋鎖的行為。以下是一個簡單的自旋鎖示例:
public class SpinLock {
private volatile boolean isLocked = false;
public void lock() {
while (true) {
if (!isLocked) { // 當鎖未被占用時
if (compareAndSet(false, true)) { // 使用CAS操作嘗試獲取鎖
break; // 成功獲取鎖后退出自旋
}
}
// 鎖被占用時,繼續(xù)循環(huán)(自旋)
}
}
public void unlock() {
isLocked = false; // 釋放鎖
}
// 使用AtomicBoolean或Unsafe等工具類提供的原子操作來實現(xiàn)compareAndSet
private boolean compareAndSet(boolean expect, boolean update) {
return java.util.concurrent.atomic.AtomicBoolean.compareAndSet(this.isLocked, expect, update);
}
// 示例用法
public static void main(String[] args) {
final SpinLock spinLock = new SpinLock();
Thread t1 = new Thread(() -> {
spinLock.lock();
try {
System.out.println("Thread 1 acquired the lock");
Thread.sleep(1000); // 模擬執(zhí)行耗時任務
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
spinLock.unlock();
}
});
Thread t2 = new Thread(() -> {
spinLock.lock();
try {
System.out.println("Thread 2 acquired the lock");
} finally {
spinLock.unlock();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}詳細解釋:
- 在上述代碼中,我們創(chuàng)建了一個名為SpinLock的類,其中包含一個volatile布爾變量isLocked,用于表示鎖的狀態(tài)。
- lock()方法通過不斷地檢查并試圖更新isLocked變量來實現(xiàn)自旋。當isLocked為false時,使用compareAndSet()原子操作將其設置為true,如果成功則說明當前線程獲得了鎖,并跳出循環(huán)。
- unlock()方法將isLocked設置為false,以釋放鎖。
- compareAndSet()方法利用Java的原子性操作(這里假設使用了AtomicBoolean)確保對isLocked變量的修改是原子性的,這可以避免多線程環(huán)境下數(shù)據(jù)競爭的問題。
九、StampedLock
Java中的StampedLock是Java 8引入的一個高性能的并發(fā)工具類,它提供了更靈活的讀寫鎖機制,包括悲觀讀鎖、樂觀讀鎖和寫鎖。每個鎖操作都會返回一個戳記(stamp),后續(xù)的操作可以通過這個戳記來驗證或釋放鎖。
以下是一個使用StampedLock實現(xiàn)讀寫鎖的示例代碼及詳細解釋:
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private final StampedLock lock = new StampedLock();
// 共享資源
private int sharedResource;
public void read() {
long stamp = lock.readLock(); // 獲取悲觀讀鎖
try {
// 在此塊中可以安全地讀取sharedResource
System.out.println("Reading the resource: " + sharedResource);
} finally {
lock.unlockRead(stamp); // 釋放讀鎖
}
}
public void optimisticRead() {
long stamp = lock.tryOptimisticRead(); // 嘗試獲取樂觀讀鎖
int localCopy = sharedResource;
if (lock.validate(stamp)) { // 驗證在獲取戳記后,數(shù)據(jù)是否被其他線程修改過
// 如果沒有被修改,則可以安全地使用localCopy
System.out.println("Optimistically reading the resource: " + localCopy);
} else {
// 如果有被修改,需要升級到悲觀讀鎖或者重新讀取
long newStamp = lock.readLock();
try {
// 現(xiàn)在可以安全地再次讀取資源
localCopy = sharedResource;
System.out.println("Upgraded to悲觀讀鎖, reading the resource: " + localCopy);
} finally {
lock.unlockRead(newStamp); // 釋放悲觀讀鎖
}
}
}
public void write(int newValue) {
long stamp = lock.writeLock(); // 獲取寫鎖
try {
// 在此塊中可以安全地更新sharedResource
sharedResource = newValue;
System.out.println("Updated the resource to: " + sharedResource);
} finally {
lock.unlockWrite(stamp); // 釋放寫鎖
}
}
// 使用示例
public static void main(String[] args) {
StampedLockExample example = new StampedLockExample();
// 創(chuàng)建并啟動多個讀取者和寫入者線程...
}
}詳細解釋:
1.StampedLock提供三種模式的鎖:
- 悲觀讀鎖:通過readLock()方法獲取,類似于ReentrantReadWriteLock中的讀鎖,當有寫鎖持有時,讀鎖會阻塞。
- 樂觀讀鎖:通過tryOptimisticRead()方法嘗試獲取,該方法立即返回,不會阻塞,但必須在之后調(diào)用validate(long stamp)方法確認讀取期間是否有寫鎖發(fā)生改變,如果數(shù)據(jù)被修改則需升級為悲觀讀鎖。
- 寫鎖:通過writeLock()方法獲取,與ReentrantReadWriteLock類似,獨占鎖,不允許任何其他讀或?qū)戞i同時存在。
2.示例中read()方法展示了如何使用悲觀讀鎖進行讀操作,在讀取期間阻止寫操作。
3.optimisticRead()方法首先嘗試樂觀讀鎖,然后檢查戳記的有效性。若數(shù)據(jù)未被更改,則可以直接使用本地緩存的值;否則,為了確保讀取到最新數(shù)據(jù),需要升級到悲觀讀鎖。
4.write()方法展示了如何使用寫鎖執(zhí)行寫操作,在寫入期間阻止所有其他讀寫操作。
總結
深入理解和熟練掌握Java中的鎖機制是構建高效、穩(wěn)定多線程程序的關鍵所在。開發(fā)者應依據(jù)具體的并發(fā)場景,權衡鎖的開銷與安全性,合理選擇并應用合適的鎖實現(xiàn)。
到此這篇關于Java鎖機制詳細講解與實戰(zhàn)應用的文章就介紹到這了,更多相關Java鎖機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
mybatis insert foreach循環(huán)插入方式
這篇文章主要介紹了mybatis insert foreach循環(huán)插入方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
SpringBoot?整合Redis?數(shù)據(jù)庫的方法
Redis是一個基于內(nèi)存的日志型可持久化的緩存數(shù)據(jù)庫,保存形式為key-value格式,Redis完全免費開源,它使用ANSI?C語言編寫。這篇文章主要介紹了SpringBoot?整合Redis?數(shù)據(jù)庫的方法,需要的朋友可以參考下2018-03-03
深入理解Java基礎之try-with-resource語法糖
這篇文章主要介紹了深入理解Java基礎之try-with-resource語法糖,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02

