背景:比如电商平台对于商品信息的上架次数远远的小于商品的读的操作。如果一个对象只能在同一时间被一个线程读很显然不符合实际的需求。
读写锁的存在:可以保证再读的时候多线程同时读并且不允许写,在写的时候只能一个线程写,并且不可读。提升效率。
package com.ydlclass.lock;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static java.util.concurrent.locks.ReentrantReadWriteLock.*;
public class ReentrantReadWriteLockTest {
//可重入的读写锁:
public static ReentrantReadWriteLock Lock = new ReentrantReadWriteLock();//这个锁可以理解位一个包含读锁与写锁的一个大的锁,可以按照需要返回需要的锁
public static int COUNT = 1;//读写操作的字段,
public static void main(String[] args) {
Runnable read = () -> {//使用匿名内部类创建一个对象,这个匿名内部类实现Runnable接口的run方法,并且创建一个实现Runnable类的类的对象。
ReadLock readLock = Lock.readLock();//使用之前创建的ReentrantReadWriteLock对象得到读锁,这个锁可以;此函数返回一个ReadLock类型的对象
//而这个对象就是一个锁,所以可以使用lock(),和unlock(),tryLock(),等Lock接口的方法
//上面的锁就是用来锁读的操作
readLock.lock();
try {
ThreadUtil.sleep(1000);//这里是假设读取数据花了两秒的时间
System.out.println("正在读数据" + COUNT);
} finally {
readLock.unlock();//lock锁接口下面的是实现类都需要对锁资源进行释放
}
};
Runnable write = () ->{
WriteLock writeLock = Lock.writeLock();//可重入锁这个大的锁可以使用函数,返回一个对象,这个对象的类型是一个WriteLock锁,
//并且这个锁实现了Lock接口的方法,所以这些锁都是拥有着lock(),unlock(),trylock(),以及其他的方法,具体可以查看Lock接口的函数
writeLock.lock();
try {
ThreadUtil.sleep(1000);
System.out.println("正在写" + COUNT++);
} finally {
writeLock.unlock();//释放这个写锁
}
};
for (int i = 0; i < 100; i++) {//此处模拟了一个常见的场景,生成100线程,并且读线程的数量大与写线程的数据。
Random random = new Random();
int flag = random.nextInt(100);
if (flag > 20) {
new Thread(read, "read").start();
} else {
new Thread(write, "write").start();
}//如果按照之前的假设,可能会认为技术是读也是两秒钟出一个读操作,即使是写也是1秒中写;但是实际情况真的如此吗?
}
//实际的体现就和之前的需求相同,只要是读可以同时的读取线程,但是对于写操作,线程必须是互斥的。
}
}