大家好,我是walker
一个从文科自学转行的程序员~
爱好编程,偶尔写写编程文章和生活
欢迎关注公众号【I am Walker】,回复“电子书”,就可以获得200多本编程相关电子书哈~
我的gitee:https://gitee.com/shen-chuhao/walker.git 里面很多技术案例!
ReadWriteLock 是什么
- 首先明确一下,不是说 ReentrantLock 不好,只是 ReentrantLock 某些时候有局限。
- 如果使用 ReentrantLock,可能本身是为了防止线程 A 在写数据、线程 B 在读数据造成的数据不一致,
- 但这样,如果线程 C 在读数据、线程 D 也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。因为这个,才诞生了读写锁 ReadWriteLock。
- ReadWriteLock 是一个读写锁接口,读写锁是用来提升并发程序性能的锁分离技术,
- ReentrantReadWriteLock 是 ReadWriteLock 接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。
读写锁的意义
ReadWriteLock 接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。
读写锁特性: 3`
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
(2)重进入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。
使用
1、基本使用案例
package lockTest.reentrantReadWriteLock;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Author: WalkerShen
* @DATE: 2022/3/22
* @Description: 读写锁
**/
public class ReadWriteLockTest {
/**
* public ReentrantReadWriteLock(boolean fair)
* ReentrantReadWriteLock也可以实现公平锁和非公平锁
*/
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final static Lock readLock = readWriteLock.readLock();
private final static Lock writeLock = readWriteLock.writeLock();
private final static List<Integer> dataList = new ArrayList<>();
public static void main(String[] args) {
new Thread(() -> write()).start();
new Thread(() -> write()).start();
new Thread(() -> read()).start();
new Thread(() -> read()).start();
}
/**
* 读方法
*/
public static void read() {
readLock.lock();
for (Integer data : dataList) {
System.out.println(Thread.currentThread().getName() + "读取数据:" + data);
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
readLock.unlock();
}
}
/**
* 写方法
*/
public static void write() {
writeLock.lock();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "写数据:" + i);
dataList.add(i);
}
writeLock.unlock();
}
}
结果:
Thread-0写数据:0
Thread-0写数据:1
Thread-0写数据:2
Thread-0写数据:3
Thread-0写数据:4
Thread-1写数据:0
Thread-1写数据:1
Thread-1写数据:2
Thread-1写数据:3
Thread-1写数据:4
Thread-2读取数据:0
Thread-2读取数据:1
Thread-2读取数据:2
Thread-3读取数据:0
Thread-2读取数据:3
Thread-3读取数据:1
Thread-2读取数据:4
Thread-3读取数据:2
Thread-2读取数据:0
Thread-3读取数据:3
Thread-2读取数据:1
Thread-3读取数据:4
Thread-2读取数据:2
Thread-3读取数据:0
Thread-2读取数据:3
Thread-3读取数据:1
Thread-2读取数据:4
Thread-3读取数据:2
Thread-3读取数据:3
Thread-3读取数据:4
从结果中可以发现,写操作的时候只有Thread-0执行完之后才到Thread-1,
而读操作的时候,Thread-2和Thread-3是交替进行的
2、锁升级
在线程中,写锁的级别是大于读锁的,意思是如果不释放读锁,去获取写锁,是获取不到的,会导致线程阻塞
但是如果持有写锁,去获取读锁,是可以获取到的
意思就是说,读锁到写锁,是需要释放后才可以升级的
package lockTest.reentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Author: WalkerShen
* @DATE: 2022/3/22
* @Description: 读写锁结合
**/
public class ReadWriteCombineTest {
private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
/**
* public static class ReadLock implements Lock, java.io.Serializable
* 因为ReadLock和WriteLock都是静态内部类,所以需要添加static修饰
*/
private static Lock readLock=readWriteLock.readLock();
private static Lock writeLock=readWriteLock.writeLock();
private static String data=null;
public static void main(String[] args) {
// new Thread(()->notReleaseReadLock()).start();
new Thread(()->releaseReadLock()).start();
}
/**
* 读写结合
* 在写锁操作的时候,没有释放读锁
* 这个时候就会阻塞
*/
public static void notReleaseReadLock(){
readLock.lock();
if(data==null){
writeLock.lock();
data="aaaa";
System.out.println(data);
writeLock.unlock();
}
readLock.unlock();
}
/**
* 读写结合
* 在获取写锁之前先释放掉读锁
* 输出结果:aaaa
*/
public static void releaseReadLock(){
readLock.lock();
if(data==null){
readLock.unlock();
writeLock.lock();
data="aaaa";
System.out.println(data);
writeLock.unlock();
}
}
}