ReadWriteLock
读写锁的实现类有:ReentrantReadWriteLock
private final ReadWriteLock lock = new ReentrantReadWriteLock(); //创建读写锁
private final Lock rLock = lock.readLock(); //获取读锁
private final Lock wLock = lock.writeLock(); //获取写锁
读写锁的特性:
可以多个线程同时获取读锁,在所有读锁释放之前,不能有线程获取写锁;
只能一个线程进行写,在写的过程中不能读;
适合读多写少的场景。
读写锁的缺点:
-
可能会造成写锁饥饿,一直读,没有写操作(可以考虑使用公平锁来创建读写锁);
ReadWriteLock lock = new ReentrantReadWriteLock(true); //基于公平锁创建ReadWriteLock
-
不适合写操作频繁的场景
使用读写锁实现多个线程操作Map的例子:
public class ReadWriteLockTest01 {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5; i++) {
int num = i;
new Thread(() -> {
myCache.put(num + "", num + "");
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
int num = i;
new Thread(() -> {
myCache.get(num + "");
}, i + "s").start();
}
}
}
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock rLock = lock.readLock();
private final Lock wLock = lock.writeLock();
public Object put(String key, Object value) {
Object result;
wLock.lock(); //上写锁,其他线程不能读也不能写
try {
System.out.println(Thread.currentThread().getName() + "正在插入:key = " + key + " value = " + value);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
result = map.put(key, value);
System.out.println(Thread.currentThread().getName() + "插入完毕:key = " + key + " value = " + value);
} finally {
wLock.unlock();
}
return result;
}
public Object get(String key) {
Object result;
rLock.lock(); //上读锁,其他线程只能读不能写
try {
System.out.println(Thread.currentThread().getName() + "正在获取:key = " + key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
result = map.get(key);
System.out.println(Thread.currentThread().getName() + "获取完毕:key = " + key + " value = " + result);
} finally {
rLock.unlock();
}
return result;
}
}
读写锁降级
在使用读写锁时,因为一个线程获取了写锁后,其他线程不能再获取读锁或写锁,所以在同一线程中,写锁可以被降级为读锁:
public class LockDemotion {
public static void main(String[] args) {
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock rLock = rwLock.readLock();
Lock wLock = rwLock.writeLock();
//获取写锁
wLock.lock();
System.out.println("写东西...");
//获取读锁
rLock.lock();
System.out.println("读东西...");
//释放写锁
wLock.unlock();
//释放读锁
rLock.unlock();
}
}
//输出:
//写东西...
//读东西...
但是我们不能反过来将读锁升级为写锁:
public class LockDemotion {
public static void main(String[] args) {
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock rLock = rwLock.readLock();
Lock wLock = rwLock.writeLock();
//获取读锁
rLock.lock();
System.out.println("读东西...");
//获取写锁
wLock.lock();
System.out.println("写东西...");
//释放读锁
rLock.unlock();
//释放写锁
wLock.unlock();
}
}
//输出:
//读东西...
可以看到,只输出了读操作,还记得读写锁的特性吗,读的时候,不能进行写操作,所以上面想锁升级反而会造成死锁。