什么是重入锁?

  • Post category:Java

一、什么是重入锁?

重入锁是一种支持在同一线程中反复获取同一把锁的锁。在使用重入锁之前,需要先获取锁,然后才能执行加锁前后的逻辑,最后再释放锁。

与 synchronized 关键字相比,重入锁具有可重入性,synchronized 是不可重入的,同一线程加锁会阻塞自己。重入锁相比 synchronized,性能更高,可以方便的实现公平锁和非公平锁,同时也支持条件变量等高级功能。

二、重入锁的使用攻略

1. 创建重入锁

在 Java 中,使用 java.util.concurrent.locks.ReentrantLock 类创建重入锁。

Lock lock = new ReentrantLock();

2. 获取锁

我们可以通过 lock() 方法获取锁,如果当前锁已被其他线程占用,则当前线程会被阻塞,直到其获得锁为止。

lock.lock();
try {
    // 执行加锁前的逻辑
} finally {
    lock.unlock();
}

3. 可重入性

对于同一个线程,可以多次获取同一个锁,而不会被阻塞。

lock.lock();
try {
    // 执行加锁前的逻辑
    lock.lock();
    try {
        // 执行加锁前的逻辑
    } finally {
        lock.unlock();
    }
} finally {
    lock.unlock();
}

4. 公平锁和非公平锁

在创建重入锁时,可以选择创建公平锁或是非公平锁。公平锁会按照线程加锁的顺序进行获取锁,而非公平锁则不保证获取锁的顺序。

// 创建公平锁
Lock fairLock = new ReentrantLock(true);
// 创建非公平锁
Lock unfairLock = new ReentrantLock(false);

5. 条件变量

与 synchronized 相比,重入锁还支持条件变量的特性。条件变量可以让一些线程在满足某些条件时才唤醒,从而改善线程的竞争情况。

Condition condition = lock.newCondition();

lock.lock();
try {
    while (!conditionSatisfied) {
        condition.await();
    }
    // 执行加锁前的逻辑
} catch (InterruptedException e) {
    // 处理异常情况
} finally {
    lock.unlock();
}

lock.lock();
try {
    conditionSatisfied = true;
    condition.signalAll();
} finally {
    lock.unlock();
}

三、重入锁的示例

1. 示例一:使用重入锁实现安全的计数器

在不使用锁的情况下,多个线程同时对计数器进行自增操作时,会造成计数器值的不确定性。而使用重入锁可以保证对计数器的操作是安全的。

public class Counter {
    private int count;
    private Lock lock = new ReentrantLock();

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

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

2. 示例二:使用重入锁实现读写锁

读写锁是一种特殊的锁,它允许多个线程同时读取共享数据,但只允许一个线程写入共享数据。

我们可以通过重入锁和 Condition 变量实现读写锁。

public class ReadWriteLock {
    private int readers;
    private int writers;
    private int writeRequests;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void lockRead() throws InterruptedException {
        lock.lock();
        try {
            while (writers > 0 || writeRequests > 0) {
                condition.await();
            }
            readers++;
        } finally {
            lock.unlock();
        }
    }

    public void unlockRead() {
        lock.lock();
        try {
            readers--;
            if (readers == 0) {
                condition.signalAll();
            }
        } finally {
            lock.unlock();
        }
    }

    public void lockWrite() throws InterruptedException {
        lock.lock();
        try {
            writeRequests++;
            while (readers > 0 || writers > 0) {
                condition.await();
            }
            writeRequests--;
            writers++;
        } finally {
            lock.unlock();
        }
    }

    public void unlockWrite() {
        lock.lock();
        try {
            writers--;
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

四、总结

以上就是重入锁的完整使用攻略。在实际开发过程中,重入锁的应用是比较常见的,其具有可重入性和高性能等多重优点,可以避免死锁等问题的出现。但是需要注意的是,使用重入锁时需要注意锁的粒度,不要出现占用锁时间过长的情况,以免影响性能。