【多线程】Java多线程基础(9)- ReadWriteLock的使用

52 阅读2分钟

ReadWriteLock

什么是读写锁(ReadWriteLock)

读写锁是一种计算机编程中用于控制共享资源访问的同步机制。它允许多个线程同时读取共享资源但确保只有一个线程能够写入该资源。在读取操作比写入操作更频繁的情况下,这可以提高性能

读写锁通常有两个与之关联的锁:读锁和写锁。多个线程可以同时获取读锁,但只有一个线程可以获取写锁当一个线程获取写锁时,所有已经获取读锁的线程都会被阻塞,直到写锁被释放。

可以通过使用可选参数进一步自定义ReadWriteLock的行为,例如读锁是否“公平”(意味着线程按照请求锁的顺序获取锁),以及写锁是否“可重入”(意味着一个线程可以多次获取写锁而不会出现死锁)。

使用ReadWriteLock可以提高需要频繁读取共享资源但不频繁写入的应用程序的性能。然而,重要的是要仔细考虑应用程序的设计,以确保使用ReadWriteLock是适当的,并且不会引入其他同步或性能问题。

怎么使用读写锁

在Java中,读写锁是通过 java.util.concurrent.locks 包中的 ReadWriteLock 接口来实现的。ReadWriteLock 接口定义了两个锁:读锁和写锁,用于控制对共享资源的访问。

使用 ReadWriteLock 的一般步骤如下:

  1. 创建 ReadWriteLock 对象:
ReadWriteLock lock = new ReentrantReadWriteLock();
  1. 获取读锁或写锁:
// 获取读锁
lock.readLock().lock();

// 获取写锁
lock.writeLock().lock();
  1. 执行读或写操作:
// 读操作
// ...

// 写操作
// ...
  1. 释放锁:
// 释放读锁
lock.readLock().unlock();

// 释放写锁
lock.writeLock().unlock();

具体案例分析

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

public class Main {
    public static void main(String[] args) {
        SharedList list = new SharedList();
        Thread thread1 = new Thread(list);
        // a1,a2,a3均为读线程
        thread1.setName("a1");
        Thread thread2 = new Thread(list);
        thread2.setName("a2");
        Thread thread3 = new Thread(list);
        thread3.setName("a3");
        // a4, a5, a6 为读写线程
        Thread thread4 = new Thread(list);
        thread4.setName("a4");
        Thread thread5 = new Thread(list);
        thread5.setName("a5");
        Thread thread6 = new Thread(list);
        thread6.setName("a6");
        // 先使用a4 写线程添加一个数据
        thread4.start();
        try {
            thread4.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 开启读线程
        thread1.start();
        thread2.start();
        thread3.start();
        // 开启写线程
        thread5.start();
        thread6.start();
    }
}

class SharedList implements Runnable{
    private ArrayList<String> list = new ArrayList<>();
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void get(){
        // 读锁
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() +"占用读锁中");
            System.out.println(Thread.currentThread().getName() + list.get(0));
        }finally {
            // 释放读锁
            lock.readLock().unlock();
        }
    }
    public void add(String value){
        // 写锁
        lock.writeLock().lock();
        try{
            list.add(value);
            System.out.println(Thread.currentThread().getName() + "占用写锁");
            System.out.println(Thread.currentThread().getName() + "操作中");
        }finally {
            // 释放写锁
            lock.writeLock().unlock();
            System.out.println(Thread.currentThread().getName() + "释放写锁");
        }
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        switch (name) {
            case "a4" : {
                add("hhh");
                break;
            }
            case "a1", "a2", "a3": {
                get();
                break;
            }
            case "a5": {
                add("ggg");
                break;
            }
            case "a6": {
                add("ooo");
            }
        }
    }
}

我创建了三个读锁,三个写锁,其中一个写锁用于添加数据。

分析

最终输出:

a4占用写锁
a4操作中
a4释放写锁
a1占用读锁中
a3占用读锁中
a2占用读锁中
a1hhh
a3hhh
a2hhh
a5占用写锁
a5操作中
a5释放写锁
a6占用写锁
a6操作中
a6释放写锁

可以看出读锁在被占用后依旧能再次被占用, 而写锁被占用后必须要释放后才能再次占用。