JUC并发编程(六)-- ReadWriteLock
程序员文章站
2022-05-04 20:51:48
...
什么是ReadWriteLock
首先我们看jdk文档:
我们可以看到,这个是读写锁,里面维护了一组锁(读锁和写锁),即读可以被多个线程同时读,而写只能有一个线程去写。这样既保证了性能,又保证了线程安全。
怎么使用
一般的ReentrantLock实现了标准的互斥锁,即当一个线程持有锁时,其他线程等待,直到当前持有锁的线程释放锁。这样虽然保证了多线程下数据的安全性,但会影响并发性能。拿两个线程来举个例子:
一般我们我们用两个线程在加了ReentrantLock锁后操作数据会有哪些情况?
- 读 <-> 读 两个线程互斥,必须一个先读,一个后读;
- 读 <-> 写 两个线程互斥,要么先写,要么先读;
- 写 <-> 写 两个线程互斥,必须一个先写,一个后写。
而我们自己思考就可以发现,其中的 读 <-> 读 没有必要加互斥锁,完全可以一起读,这样可以提高性能。这个时候就需要 ReadWriteLock
- 读 <-> 读 两个线程可以共存,同时读;
- 读 <-> 写 两个线程互斥,要么先写,要么先读;
- 写 <-> 写 两个线程互斥,必须一个先写,一个后写。
上代码:
package com.zhan.juc.lock;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Author Zhanzhan
* @Date 2020/12/3 21:07
* 读写锁
* 读 - 读 线程可以共存
* 读 - 写 线程互斥,不能共存
* 写 - 写 线程互斥,不能共存
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 写入操作
for (int i = 0; i < 5; i++) {
int temp = i;
new Thread(() -> {
myCache.put((temp + 1) + "", "value");
}, String.valueOf(i + 1)).start();
}
for (int i = 0; i < 5; i++) {
int temp = i;
new Thread(() -> {
myCache.get((temp + 1) + "");
}, String.valueOf(i + 1)).start();
}
}
}
/**
* 自定义一个缓存类
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 写入缓存
*
* @param key
* @param value
*/
public void put(String key, Object value) {
readWriteLock.writeLock().lock(); // 加 写锁
try {
System.out.println("线程 " + Thread.currentThread().getName() + " 写入" + key);
map.put(key, value);
System.out.println("线程 " + Thread.currentThread().getName() + " 写入完成!");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();// 释放 写锁
}
}
/**
* 从缓存中读取数据
*
* @param key
* @return
*/
public Object get(String key) {
readWriteLock.readLock().lock();// 加 读锁
try {
System.out.println("线程 " + Thread.currentThread().getName() + " 读取" + key);
Object o = map.get(key);
System.out.println("线程 " + Thread.currentThread().getName() + " 读取完成!");
return o;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
readWriteLock.readLock().unlock();// 释放读锁
}
}
}
运行结果:
我们看到,写入和读取 以及 写入和写入的时候,线程之间都没有插队,只有读和读之间,才有线程插队。符合预期!
上一篇: 短而精悍的几则冷幽默
推荐阅读
-
[书籍翻译] 《JavaScript并发编程》第六章 实用的并发
-
JAVA并发编程(六):线程本地变量ThreadLocal与TransmittableThreadLocal
-
Java并发线程编程基础(3)-ReadWriteLock读写锁
-
【2020Python修炼记】python并发编程(六)补充—进程池和线程池
-
JUC并发编程(concurrent包的介绍)
-
并发编程(六)——AbstractQueuedSynchronizer 之 Condition 源码分析
-
详解JUC并发编程之锁
-
java并发编程工具类JUC之ArrayBlockingQueue
-
Java并发编程JUC源码学习之ThreadPoolExecutor
-
《Java并发编程的艺术》第六章——Java并发容器和框架