技术学习笔记
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode

4.2 ReentrantReadWriteLock

读写锁包括共享锁(读)、排他锁(写)。

没有线程进行写入操作时,多个读取线程都可以获得锁,而进行写入操作的线程只有获得写入锁才能写入。

  • 读读共享
  • 写写互斥
  • 读写互斥
  • 写读互斥

读读共享

Service.java
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service {
    private ReentrantReadWriteLock lock;

    public Service(ReentrantReadWriteLock lock){
        this.lock = lock;
    }

    public void read(){
        lock.readLock().lock();
        System.out.println(Thread.currentThread().getName() + " " +System.currentTimeMillis());
        lock.readLock().unlock();
    }
}
ThreadA.java
public class ThreadA extends Thread{
    private Service service;

    public ThreadA(Service service){
        this.service = service;
    }
    @Override
    public void run(){
        service.read();
    }
}
ThreadB.java
public class ThreadB extends Thread{
    private Service service;

    public ThreadB(Service service){
        this.service = service;
    }
    @Override
    public void run(){
        service.read();
    }
}

Run.java
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Run {
    public static void main(String[] args) {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        Service service = new Service(lock);
        ThreadA A = new ThreadA(service);
        ThreadB B = new ThreadB(service);
        A.start();
        B.start();
    }
}

执行结果

Thread-0 1632923125926
Thread-1 1632923125926

两个线程打印的间隔时间很短,几乎同时访问共享锁锁住的内容,这样的共享锁可以提高程序的运行效率。

写写互斥

把读锁换成写锁 lock.writeLock.lock(),则 A B两个线程执行 service.read() 进行的是写锁(排他锁)。

运行结果:

Thread-0 1632923636440
Thread-1 1632923636451

可以发现两个线程打印的间隔较长(单位毫秒)。

读写互斥

Service.java
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service {
    private ReentrantReadWriteLock lock;
    public Service(ReentrantReadWriteLock lock){
        this.lock = lock;
    }
    public void read(){
        try{
            lock.readLock().lock();
            System.out.println("read:" + Thread.currentThread().getName() + " " +System.currentTimeMillis());
            Thread.sleep(3000);
        }catch (InterruptedException e){
        }finally {
            lock.readLock().unlock();
        }
    }
    public void write(){
        try {
            lock.writeLock().lock();
            System.out.println("write:"+ Thread.currentThread().getName() + " " +System.currentTimeMillis());
            Thread.sleep(3000);
        }catch (InterruptedException e){
        }finally {
            lock.writeLock().unlock();
        }

    }
}

ThreadA.java
public class ThreadA extends Thread{
    private Service service;

    public ThreadA(Service service){
        this.service = service;
    }
    @Override
    public void run(){
        service.read();
    }
}
ThreadB.java
public class ThreadB extends Thread{
    private Service service;

    public ThreadB(Service service){
        this.service = service;
    }
    @Override
    public void run(){
        service.write();
    }
}

Run.java
public class Run {
    public static void main(String[] args) throws InterruptedException {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        Service service = new Service(lock);
        ThreadA A = new ThreadA(service);
        ThreadB B = new ThreadB(service);
        A.start();
        Thread.sleep(1000);
        B.start();
    }
}

运行结果:

read:Thread-0 1632924236688
write:Thread-1 1632924239709

线程 A 在读取时,线程 B 想要写入,二者之间是互斥的。

写读互斥

将 Run.java 中的 线程 A 和线程 B 的执行顺序颠倒,也会产生互斥。

A.start();
Thread.sleep(1000);
B.start();