天天看點

java中有幾種鎖的實作方式synchronized與其他實作有什麼差別

作者:團結tech

Java中有以下幾種鎖的實作方式:

  1. synchronized關鍵字:synchronized是Java語言内置的一種鎖機制,用于實作線程同步,防止多個線程同時通路同一代碼塊或方法。synchronized可以用來修飾方法、代碼塊或靜态方法,保證它們在同一時刻隻有一個線程能夠執行。
  2. ReentrantLock類:ReentrantLock是Java.util.concurrent包中提供的一種可重入的互斥鎖。它比synchronized關鍵字更加靈活,可以實作公平鎖或非公平鎖,支援可中斷的鎖等特性。ReentrantLock也可以用來修飾代碼塊或方法。
  3. ReadWriteLock類:ReadWriteLock是Java.util.concurrent包中提供的讀寫鎖機制。它允許多個線程同時讀取共享資料,但在寫入資料時必須互斥。ReadWriteLock包含一個讀鎖和一個寫鎖,讀鎖可以被多個線程同時持有,但寫鎖隻能被一個線程持有。
  4. StampedLock類:StampedLock是Java.util.concurrent包中提供的一種樂觀鎖機制。它比ReadWriteLock更加靈活和高效,在某些場景下可以替代ReentrantLock和ReadWriteLock。StampedLock包含三種模式:讀模式、寫模式和樂觀讀模式,它們之間互斥,但樂觀讀模式不會阻塞寫線程,是以效率更高。

在選擇鎖機制時,需要考慮到線程安全、并發度、性能和可維護性等因素。以下是一些示例,說明了如何選擇不同的鎖機制。

  • 對于并發度較低的情況,可以使用synchronized關鍵字,它簡單易用,能夠滿足大部分的同步需求。例如:
public synchronized void increment() {
    count++;
}           
  • 對于并發度較高、線程等待時間較長的情況,可以選擇ReentrantLock,它可以實作公平鎖或非公平鎖,支援可中斷的鎖等特性。例如:
private ReentrantLock lock = new ReentrantLock();

public void increment() {
    lock.lock();
    try {
        count++;
    } finally {
        lock.unlock();
    }
}
           
  • 對于讀多寫少的情況,可以選擇ReadWriteLock,它允許多個線程同時讀取共享資料,但在寫入資料時必須互斥。例如:
private ReadWriteLock lock = new ReentrantReadWriteLock();

public void read() {
    lock.readLock().lock();
    try {
        // 讀取共享資料
    } finally {
        lock.readLock().unlock();
    }
}

public void write() {
    lock.writeLock().lock();
    try {
        // 寫入共享資料
    } finally {
        lock.writeLock().unlock();
    }
}
           
  • 對于高性能、低競争的情況,可以選擇StampedLock,它比ReentrantLock和ReadWriteLock更加高效。例如
private StampedLock lock = new StampedLock();

public void read() {
    long stamp = lock.tryOptimisticRead();
    // 讀取共享資料
    if (!lock.validate(stamp)) {
        stamp = lock.readLock();
        try {
            // 重新讀取共享資料
        } finally {
            lock.unlockRead(stamp);
        }
    }
}

public void write() {
    long stamp = lock.writeLock();
    try {
        // 寫入共享資料
    } finally {
        lock.unlockWrite(stamp);
    }
}
           

不同的鎖機制在不同的場景下具有不同的性能表現,是以很難說哪種鎖機制的效率最好。以下是一些常見的鎖機制的性能特點:

  1. synchronized:在Java 6及以前的版本中,synchronized的性能比較低;而在Java 6及以後的版本中,通過一系列的優化,synchronized的性能得到了很大的提升。當然,synchronized的性能仍然比不上其他一些進階的鎖機制。
  2. ReentrantLock:ReentrantLock的性能比synchronized略高,在并發度較高的情況下效果更加明顯。但是,ReentrantLock的使用比較複雜,需要手動加鎖和解鎖,并且要注意鎖的釋放問題。
  3. ReadWriteLock:在讀多寫少的情況下,ReadWriteLock的性能比較好,因為它允許多個線程同時讀取共享資料。但是,在寫入資料時需要互斥,是以寫操作的性能會受到影響。
  4. StampedLock:StampedLock是Java 8中新增的一種鎖機制,它比ReentrantLock和ReadWriteLock更加高效。StampedLock的優勢在于它提供了樂觀鎖和悲觀鎖兩種模式,可以根據實際需求進行選擇。

synchronized因為是java保留字,相對于其他鎖機制是在java語言層面實作的鎖機制,随着java版本更新,其有用更多性能提升的可能性。比如在Java 6中,對synchronized進行了一些優化,主要包括以下幾個方面:

  1. 輕量級鎖(Lightweight Locking):在競争不激烈的情況下,synchronized會使用輕量級鎖代替傳統的重量級鎖,以減小鎖的粒度,提高并發性能。
  2. 自适應自旋鎖(Adaptive Spinning):在競争激烈的情況下,synchronized會自适應地使用自旋鎖,以避免線程因等待鎖而被阻塞,進而提高并發性能。
  3. 鎖消除(Lock Elimination):當編譯器發現某些代碼塊不存在競争條件時,會将其中的鎖自動消除,以避免不必要的鎖競争。
  4. 偏向鎖(Biased Locking):在鎖對象一直被同一線程持有的情況下,會自動将該鎖對象轉換為偏向鎖,以避免多次加鎖解鎖操作,進而提高并發性能。

這些優化使得synchronized的性能得到了很大的提升,進而使得synchronized成為Java中最常用的鎖機制之一。