并发编程系列之ReadWriteLock使用
并发编程系列之ReadWriteLock使用
1、什么是ReadWriteLock?
ReadWriteLock是jdk的juc包中提供的读写锁api,维护一对关联的读锁、写锁,读锁可以被多个读线程共享,写锁排他。
2、为什么需要ReadWriteLock?
之前我们喜欢使用ReentrantLock,重入锁,既然提供了ReentrantLock这个api,jdk官方又推出ReadWriteLock,相对ReentrantLock来说,ReadWriteLock是比较省资源的,ReentrantLock虽然也可以保证线程安全,但是比较耗资源的,比如在所有线程都是读锁的情况,这种情况就是线程安全的,就不需要做线程安全控制,直接保证线程并行执行就行,但是ReentrantLock不能做到,所以ReadWriteLock根据一系列规则保证了线程安全也保证了执行效率。
3、ReadWriteLock的常用API
ReadWriteLock在jdk8的juc包里有两个实现类:
ReentrantReadWriteLock的主要方法:
4、读写锁的获取规则
- 如果一个线程已经占用了读锁,其他线程想要申请读锁,是可以申请成功的
- 如果一个线程已经占用了读锁,其他线程想要申请写锁,是不可以申请成功的
- 如果一个线程已经占用了写锁,其他线程想要申请读锁还是写锁,都是不可以申请成功的
总结起来就是:读读共享、其他都互斥(写写互斥、读写互斥、写读互斥)
5、ReadWriteLock的适用场景
知道了ReadWriteLock的特效之后,我们知道相比于 ReentrantLock 适用于一般场合,ReadWriteLock 适用于读多写少的情况,合理使用可以进一步提高并发效率
6、ReadWriteLock的例子
例子,使用ReentrantReadWriteLock
创建读锁和写锁
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public static void read() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName()+"得到了读锁");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName()+"释放读锁");
readLock.unlock();
}
}
public static void write() {
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "得到了写锁");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() +"释放写锁");
writeLock.unlock();
}
}
public static void main(String[] args) {
new Thread(() -> read()).start();
new Thread(() -> read()).start();
new Thread(() -> read()).start();
new Thread(() -> write()).start();
new Thread(() -> write()).start();
new Thread(() -> write()).start();
}
}
控制台打印,从打印结果可以看出,读锁可以同时被多个线程获得的,一个线程获取到读锁之后,另外一个线程又获取来了,而写锁必须等线程释放之后,才可以被其他线程获取,所以读锁共享,写锁排他
Thread-0得到了读锁
Thread-1得到了读锁
Thread-1释放读锁
Thread-0释放读锁
Thread-3得到了写锁
Thread-3释放写锁
Thread-2得到了读锁
Thread-2释放读锁
Thread-4得到了写锁
Thread-4释放写锁
Thread-5得到了写锁
Thread-5释放写锁
锁降级:指的是写锁降级成为读锁。把持住当前写锁的同时,再获取读锁,随后释放写锁的过程。写锁是线程独占的,读锁是共享的。所以写锁降级为读锁是降级。读锁升级为写锁不能实现。例子来自网上,稍作修改:
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockCachedDataExample {
class Data {}
class RWDictionary {
private final Map<String, Data> map = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock rLock = readWriteLock.readLock();
private final Lock wLock = readWriteLock.writeLock();
public Data get(String key) {
rLock.lock();
try {
return map.get(key);
}finally {
rLock.unlock();
}
}
public Data put(String key , Data value) {
wLock.lock();
try {
return map.put(key , value);
}finally {
wLock.unlock();
}
}
public String[] allKeys() {
rLock.lock();
try {
return (String[]) map.keySet().toArray();
}
finally {
rLock.unlock();
}
}
public void clear() {
wLock.lock();
try {
map.clear();
}
finally {
wLock.unlock();
}
}
}
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = getData();
cacheValid = true;
}
// 【降级】Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
}finally {
rwl.writeLock().unlock();
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
private Object getData() {
return null;
}
void use(Object data){}
}
}
附录参考资料
相关文章
- Jease 2.6发布 Java开源内容框架
- JVM调优总结:反思
- JVM调优总结:调优方法
- JVM调优总结:新一代的垃圾回收算法
- JVM调优总结:典型配置举例
- JVM调优总结:分代垃圾回收详述
- JVM调优总结:垃圾回收面临的问题
- JVM调优总结:基本垃圾回收算法
- JVM调优总结:一些概念
- 用Java GUI编写的画板程序
- Java的动态绑定机制
- jOOQ 2.0.2发布 Java的ORM框架
- Java中带复选框的树的实现和应用
- Java网络编程菜鸟进阶:TCP和套接字入门
- 甲骨文与谷歌专利权之争定于今年三月开审
- Java调用C/C++编写的第三方dll动态链接库
- 集成开发环境 NetBeans IDE 7.1正式版发布
- kangle 2.7.5紧急发布 防hash碰撞攻击
- 东方通技术引领模式为国产软件“争权”
- UML中关联,组合与聚合等关系的辨析