1、痛点
多个线程同时获取一个共享资源的时候,是不会有什么问题。但是一个线程要往共享资源写入数据时,就得加上锁,不允许其他线程读或者写。
可以对资源的写入和读取方法进行加锁,但是会带来一个问题:一个线程在读资源数据,其他要读资源的线程也只能等待。
读写锁,就是解决这个痛点的方案。
对于资源的读取和写入,采取两把锁,分别称为读锁和写锁。当然了,读写锁的读和写也是互斥的,即写的时候不能读,读的时候不能写。它的优点在于:写锁只有一个,但读锁不止一个,每个线程进行读操作时,都可以分配到一把读锁,也就是多线程同时读取资源时不会互斥。
2、ReadWriteLock
ReadWriteLock
是java.util.concurrent.locks
包下的一个接口,称为读写锁,它有唯一的实现类ReentrantReadWriteLock
,称为可重入读写锁。
它有两个方法,分别是获取读锁和写锁:
写的时候只有一个线程可以操作,读的时候可以多个线程一起多,实现读写锁分离:
3、读写锁的使用
创建一个缓存类,里面有读和写两个方法:
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyCacheLock {
private volatile Map<String, Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t 正在写" + key);
//暂停一会儿线程
TimeUnit.MILLISECONDS.sleep(300);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 写完了" + key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
public void get(String key) {
readWriteLock.readLock().lock();
try {
Object result = null;
System.out.println(Thread.currentThread().getName() + "\t 正在读" + key);
TimeUnit.MILLISECONDS.sleep(300);
result = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t 读完了" + result);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
}
线程操作资源类:
public class ReentrantReadWriteLockTest {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.put(num + "", num + "");
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.get(num + "");
}, String.valueOf(i)).start();
}
}
}
输出结果: