ReadWriteLock
什么是读写锁(ReadWriteLock)
读写锁是一种计算机编程中用于控制共享资源访问的同步机制。它允许多个线程同时读取共享资源,但确保只有一个线程能够写入该资源。在读取操作比写入操作更频繁的情况下,这可以提高性能。
读写锁通常有两个与之关联的锁:读锁和写锁。多个线程可以同时获取读锁,但只有一个线程可以获取写锁。当一个线程获取写锁时,所有已经获取读锁的线程都会被阻塞,直到写锁被释放。
可以通过使用可选参数进一步自定义ReadWriteLock的行为,例如读锁是否“公平”(意味着线程按照请求锁的顺序获取锁),以及写锁是否“可重入”(意味着一个线程可以多次获取写锁而不会出现死锁)。
使用ReadWriteLock可以提高需要频繁读取共享资源但不频繁写入的应用程序的性能。然而,重要的是要仔细考虑应用程序的设计,以确保使用ReadWriteLock是适当的,并且不会引入其他同步或性能问题。
怎么使用读写锁
在Java中,读写锁是通过 java.util.concurrent.locks
包中的 ReadWriteLock
接口来实现的。ReadWriteLock
接口定义了两个锁:读锁和写锁,用于控制对共享资源的访问。
使用 ReadWriteLock
的一般步骤如下:
- 创建
ReadWriteLock
对象:
ReadWriteLock lock = new ReentrantReadWriteLock();
- 获取读锁或写锁:
// 获取读锁
lock.readLock().lock();
// 获取写锁
lock.writeLock().lock();
- 执行读或写操作:
// 读操作
// ...
// 写操作
// ...
- 释放锁:
// 释放读锁
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释放写锁
可以看出读锁在被占用后依旧能再次被占用, 而写锁被占用后必须要释放后才能再次占用。