返回

秒懂!锁的内幕:轻量级、重量级,该怎么选?

后端

锁:保证并发访问共享数据的数据结构

在软件开发中,锁是一种至关重要的数据结构,它可以协调多个线程或进程同时访问共享数据,防止数据损坏或不一致。

锁的类型

根据开销和并发性,锁可以分为两类:

  • 轻量级锁: 开销较小,适用于并发不激烈的场景。通常使用(如Java中的synchronized)或轻量级锁类实现。
  • 重量级锁: 开销较大,但可以保证更高的并发性。通常使用显式锁(如Java中的ReentrantLock)实现。

Java中的锁实现

synchronized:轻量级锁

synchronized关键字可修饰方法或代码块,同一时刻只能有一个线程执行被修饰的内容。其内部使用监视器对象控制访问,线程获取锁后才能进入受保护的代码。

ReentrantLock:重量级锁

ReentrantLock是一个显式锁类,提供手动获取和释放锁的功能。它使用抽象队列同步器(AQS)控制对共享数据的访问。线程必须调用lock()方法获取锁,并在使用完毕后调用unlock()方法释放锁。

Redis分布式锁

Redis分布式锁利用Redis的SETNX命令,尝试设置一个键值对。如果键值对不存在,则设置成功,线程获取锁;否则,获取锁失败。

锁的选取

轻量级锁和重量级锁各有优势,应根据实际场景选择合适的类型。

  • 轻量级锁: 适用于并发不激烈的场景,开销较小。
  • 重量级锁: 适用于高并发场景,开销较大但并发性更高。

示例代码

Java中使用synchronized:

public class SynchronizedCounter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

Java中使用ReentrantLock:

public class ReentrantLockCounter {
    private int count = 0;
    private ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

Redis分布式锁:

public class RedisDistributedLock {
    private Jedis jedis;

    public RedisDistributedLock(Jedis jedis) {
        this.jedis = jedis;
    }

    public boolean lock(String key, String value) {
        return jedis.setnx(key, value) == 1;
    }

    public void unlock(String key, String value) {
        if (jedis.get(key).equals(value)) {
            jedis.del(key);
        }
    }
}

常见问题解答

  1. 什么是死锁? 死锁是指两个或多个线程互相等待对方的锁,导致所有线程都无法继续执行。
  2. 如何避免死锁? 避免死锁的策略包括按特定顺序获取锁、使用超时机制或采用死锁检测和恢复机制。
  3. 何种锁适用于我的应用程序? 根据并发级别和性能要求,轻量级锁或重量级锁可能更合适。
  4. Redis分布式锁是如何实现的? Redis分布式锁使用SETNX命令设置一个键值对,如果键值对不存在则获取锁成功。
  5. 如何确保分布式锁的可靠性? 可以通过使用哨兵或集群模式等Redis复制机制来提高分布式锁的可靠性。