25 Rules for Acquiring Locks With Read Write Lock

25 Rules for Acquiring Locks with ReadWriteLock #

In this lesson, we will mainly discuss the rules for acquiring locks using ReadWriteLock.

Before the introduction of ReadWriteLock, let’s assume we were using a regular ReentrantLock. Although we ensure thread safety, we also waste some resources because if multiple read operations are performed simultaneously, there is no thread safety issue. We can allow multiple read operations to proceed in parallel to improve program efficiency.

However, write operations are not thread-safe. If multiple threads write simultaneously or perform read operations while writing, it will cause thread safety issues.

Our ReadWriteLock solves this problem by setting a set of rules to ensure both efficiency when multiple threads are reading and thread safety when writing.

The overall idea is that it has two locks: a write lock and a read lock. After acquiring the write lock, we can both read and modify data. In contrast, after acquiring the read lock, we can only view data and not modify it. The read lock can be held by multiple threads at the same time, so multiple threads can view data simultaneously.

Reasonably use the read lock where reading is required and use the write lock where writing is required, flexibly controlling them to improve program execution efficiency.

Rules for acquiring ReadWriteLock #

When using ReadWriteLock, we follow the following acquisition rules:

  1. If a thread has acquired a read lock, other threads can successfully acquire a read lock at this time.
  2. If a thread has acquired a read lock, other threads that want to acquire a write lock will wait until the read lock is released, because reading and writing cannot be performed simultaneously.
  3. If a thread has acquired a write lock, other threads that want to acquire a write lock or a read lock must wait for the previous thread to release the write lock because reading and writing cannot be performed simultaneously, and two threads should not write simultaneously.

So, in summary, either one or more threads have read locks concurrently, or one thread has a write lock, but the two will not appear simultaneously. It can also be summarized as: read-read sharing, the rest are mutually exclusive (write-write mutual exclusion, read-write mutual exclusion, and write-read mutual exclusion).

Usage examples #

Now let’s take an example to apply ReadWriteLock. ReentrantReadWriteLock is an implementation of ReadWriteLock, and the most important methods are readLock() and writeLock() used to acquire read locks and write locks.

The code is as follows:

/**

 * Description: Demonstrate the usage of ReadWriteLock.

 */

public class ReadWriteLockDemo {

    private static final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(

            false);

    private static final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock

            .readLock();

    private static final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock

            .writeLock();

    private static void read() {

        readLock.lock();

        try {

            System.out.println(Thread.currentThread().getName() + " acquires read lock, reading...");

            Thread.sleep(500);

        } catch (InterruptedException e) {

            e.printStackTrace();

        } finally {

            System.out.println(Thread.currentThread().getName() + " releases read lock");

            readLock.unlock();

        }

    }

    private static void write() {

        writeLock.lock();

        try {

            System.out.println(Thread.currentThread().getName() + " acquires write lock, writing...");

            Thread.sleep(500);

        } catch (InterruptedException e) {

            e.printStackTrace();

        } finally {

            System.out.println(Thread.currentThread().getName() + " releases write lock");

            writeLock.unlock();

        }

    }

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> read()).start();

        new Thread(() -> read()).start();

        new Thread(() -> write()).start();

        new Thread(() -> write()).start();

    }

}

The program’s output is as follows:

Thread-0 acquires read lock, reading...

Thread-1 acquires read lock, reading...

Thread-0 releases read lock

Thread-1 releases read lock

Thread-2 acquires write lock, writing...

Thread-2 releases write lock

Thread-3 acquires write lock, writing...

Thread-3 releases write lock

It can be seen that multiple threads can obtain read locks simultaneously, but write locks cannot.

Applicable scenarios for ReadWriteLock #

Finally, let’s take a look at the applicable scenarios for ReadWriteLock. Compared to ReentrantLock, which is suitable for general scenarios, ReadWriteLock is suitable for scenarios where reading is more frequent than writing. Proper usage can further improve concurrency efficiency.