跳至主要內容

并发包里的锁总结

fangzhipeng约 1326 字大约 4 分钟

并发包的锁

并发包有三种常见的锁,分别是ReentrantLock、ReadWriteLock、StampedLock等。

image-20231223200719658
image-20231223200719658

它们的主要区别和特点:

  1. ReentrantLock(可重入锁):

    • 特点:ReentrantLock 是一种独占锁(排他锁),允许同一个线程多次获取同一把锁(可重入性)。
    • 适用场景:适用于需要更灵活的锁操作、对锁的粒度要求较高、需要条件等待的场景。
    • 特点:它提供了独占的锁,不支持读写锁的共享特性。
  2. ReadWriteLock(读写锁):

    • 特点:ReadWriteLock 使用读锁和写锁分离,允许多个线程同时获取读锁,但只允许一个线程获取写锁。
    • 适用场景:适用于读多写少的场景,能够提高读操作的并发性能,对写操作加了排他性的限制。
    • 特点:支持读锁的共享特性,不适合处理写操作频繁的场景。
  3. StampedLock(标记锁):

    • 特点:StampedLock 是 Java 8 新引入的锁机制,是读写锁的扩展,支持乐观读锁(不阻塞其他读写操作)、悲观读锁和写锁。
    • 适用场景:适用于读操作远远多于写操作的场景,并且乐观读操作较为频繁的情况。
    • 特点:支持乐观读和悲观读、写操作,适合读多写少的场景,能够提供更好的并发性能。

ReentrantLock

ReentrantLock是Java并发包中提供的一种可重入互斥锁(Reentrant Mutual Exclusion Lock)。它是一种独占锁,也称为独占模式的锁。

使用ReentrantLock实现一个并发安全的缓存,示例代码如下:

package io.github.forezp.concurrentlab.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo3 {

    private Map<String, Object> cache = new HashMap<>();

    private final ReentrantLock lock = new ReentrantLock();//默认非公平锁

    
    public void put(String key, Object value) {
        try {
            lock.lock();
            cache.put(key, value);
        } finally {
            lock.unlock();
        }
    }

    public Object get(String key) {
        try {
            lock.lock();
            return cache.get(key);
        } finally {
            lock.unlock();
        }
    }
}

ReadWriteLock

ReadWriteLock是Java并发包中提供的一种读写锁机制,它以更细粒度的方式控制对共享资源的并发访问。

ReadWriteLock的特点包括:

  1. 读锁共享,写锁独占:多个线程可以同时获取读锁,实现读并发性能的提升。当一个线程获取写锁时,它会独占资源,阻塞其他线程的读和写操作。

  2. 公平性选择:根据创建ReadWriteLock时的参数,可以选择是否使用公平策略。

  3. 可重入性:和ReentrantLock一样,读锁和写锁都是可重入的

  4. 读写分离:ReadWriteLock将读锁和写锁分离,这样可以允许多个读线程同时获取读锁,但只允许一个写线程获取写锁。

使用ReadWriteLock实现一个并发安全的缓存,示例代码如下:

public class ReadWriteLockDemo {

    private Map<String, Object> cache = new HashMap<>();

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();//默认非公平锁
    Lock readLock = readWriteLock.readLock();//读锁和写锁是互斥的
    Lock writeLock = readWriteLock.writeLock();


    public void put(String key, Object value) {
        try {
            writeLock.lock();
            cache.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }

    public Object get(String key) {
        try {
            readLock.lock();
            return cache.get(key);
        } finally {
            readLock.unlock();
        }
    }

}

StampedLock

StampedLock是Java 8中引入的一种新的并发锁机制,属于读写锁的一种扩展。它比ReentrantLock和ReadWriteLock更加灵活和高效,适用于读多写少的场景。

StampedLock支持三种模式的访问:

  1. 写模式(Write Lock):与传统的独占写锁类似,一个线程获取了写锁后,其他线程无法同时获取读锁或写锁。写模式是排它的,保证了数据的一致性。

  2. 读模式(Read Lock):多个线程可以同时获取读锁,在没有线程持有写锁或等待的写锁时,读模式是共享的。读锁不会阻塞读线程,只有在有线程持有写锁时,读线程会被阻塞。

  3. 乐观读模式(Optimistic Read):**乐观读是一种特殊的读模式,与读锁类似,它允许多个线程同时进入,但不会引起冲突。**乐观读并不会阻塞其他线程获取写锁,因此是一种快速的读操作。在使用乐观读结果进行后续操作前,需要使用validate方法验证数据是否被其他线程修改。

使用StampedLock可以在读多写少的情况下提供更好的性能,但使用时需要注意乐观读操作可能会发生写冲突,需要使用validate方法进行数据验证。此外,StampedLock并不支持可重入的读模式。

使用StampedLock实现一个并发安全的缓存,示例代码如下:

package io.github.forezp.concurrentlab.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.StampedLock;

/**
 * StampedLock 的性能之所以比 ReadWriteLock 还要好,
 * 其关键是 StampedLock 支持乐观读的方式。ReadWriteLock
 * 支持多个线程同时读,但是当多个线程同时读的时候,所有的写操作会被阻塞;
 * 而 StampedLock 提供的乐观读,是允许一个线程获取写锁的,
 * 也就是说不是所有的写操作都被阻塞。
 */
public class StampedLockDemo {

    private Map<String, Object> cache = new HashMap<>();

    final StampedLock sl = new StampedLock();//不可重入锁


    public Object get(String key) {
        long stamp = sl.tryOptimisticRead();
        Object result = cache.get(key);
        if (!sl.validate(stamp)) {
            try {
                stamp = sl.tryReadLock();
                result = cache.get(key);
            } finally {
                sl.unlock(stamp);
            }
        }
        return result;

    }

    public void write(String key, Object value) {
        long stamp = 0;
        try {
            stamp = sl.writeLock();
            cache.put(key, value);
        } finally {
            sl.unlock(stamp);
        }
    }
}
方志朋_官方公众号
方志朋_官方公众号