文章目录
- Java多线程相关知识【12】--设计模式--读写锁模式(读写模式)
- 1.问题的引入
- 产生问题的代码
- 2.解决方案
- 解决的原理
- 代码实现
- 读写锁的实现
- 3.进阶解决方案
- 问题的引入
- 解决的原理
- 代码实现
- 读写锁
- 共享数据
- 读线程
- 写线程
- 读写锁的缺陷
- 缺陷的分析
- 缺陷的解决
- 解决的实现代码
- 读写锁改进
Java多线程相关知识【12】–设计模式–读写锁模式(读写模式)
1.问题的引入
假设现在有一个景点,而这个景点也只有一个验票员,他的工作就是拿到客户的票,撕掉门票的副票,把除了副票的部分还给游客。在平常的时候,客流量并不是很大,所以,他并不会出错。
然而某天,这个景点突然受到了特别高的宣传,一下子有很多人都蜂拥到了这个景点。
随着客流量的加大,和长时间的疲劳工作,这个验票员终于没有办法顶住客流量的考验,有的游客直接把票交给了他,而他并没有及时的把票还给游客,而后来的游客又将他的票递了上来,然后,接下来,他的工作就乱了套了,一下子就手忙脚乱了起来。
由于出现了工作的失误,他被老板扣了工资。
而在Java多线程中,这样的问题可能是由于多个线程同时操作共享数据时,产生的问题。
产生问题的代码
/**
* 未加锁而多线程同时操作共享变量
*/
public class NoLockToWork {
private String mainData;
private String lastData;
private int i=0;
public void check(String d,String f) throws InterruptedException {
mainData=d;
lastData=f;
i++;
verify();
}
private void verify() throws InterruptedException {
if(!mainData.equals(lastData)){
System.out.print("***********error*************");
System.out.println("NO."+i+" "+mainData+" is "+lastData);
wait();
}
}
public static void main(String[] args) {
NoLockToWork work=new NoLockToWork();
new Thread(()->{
while (true){
try {
work.check("10000","10000");
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
while (true){
try {
work.check("20000","20000");
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
while (true){
try {
work.check("30000","30000");
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
2.解决方案
解决的原理
就上面问题的出现,主管人员想到了一个解决办法,即主管拿着扣下验票员的工资,又请了多个警卫,在园区的各个位置,同时,安排了两个警卫,帮助验票员进行验票的秩序维护。
在java中,我们就可以对相应的需要读写的进程进行加锁,从而进行保护。
代码实现
读写锁的实现
/**
* 加线程锁后代码将不会出错
*/
public class AddLockToWork {
private String mainData;
private String lastData;
private int i=0;
public void check(String d,String f) throws InterruptedException {
mainData=d;
lastData=f;
i++;
verify();
}
//在此位置加锁(对函数加锁)
private synchronized void verify() throws InterruptedException {
if(!mainData.equals(lastData)){
System.out.print("***********error*************");
System.out.println("NO."+i+" "+mainData+" is "+lastData);
wait();
}
}
public static void main(String[] args) {
NoLockToWork work=new NoLockToWork();
new Thread(()->{
while (true){
try {
work.check("10000","10000");
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
while (true){
try {
work.check("20000","20000");
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
while (true){
try {
work.check("30000","30000");
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
3.进阶解决方案
问题的引入
就景点事件继续说起,由于进入园区后,每个人参观一个景点时,警卫都会严格的维护秩序,这也就使得了园区内部每个景点每天的游客量急剧减少,这也就愁坏了经理。
解决的原理
经理仔细的分析了产生这种情况的问题,由于园区内部,人们参观景点时,并不需要再次查票,而使用大量的保安继续维护秩序将会降低效率,固经理减少了园区内景点前的保安工作。
在java中,这种情况的产生就相当于有读数据的程序,也有写数据的程序,而多个人是可以同时读的,然而,同时只能有一个人进行数据的写操作。
具体可进行的操作如下:
正在运行的程序性质 | 将要运行的程序性质 | 可读 | 是否要加读锁 | 可写 | 是否要加写锁 |
读操作 | 读操作 |
|
| - | - |
写操作 | - | - |
|
| |
写操作 | 读操作 |
|
| - | - |
写操作 | - | - |
|
|
代码实现
读写锁
/**
* 读写锁核心
*/
public class ReadWriteLock {
/**
* 等待读的进程数
*/
private int waitReading = 0;
/**
* 工作读的进程数
*/
private int workReading = 0;
/**
* 等待写的进程数
*/
private int waitWriting = 0;
/**
* 工作写的进程数(最多只有一个)
*/
private int workWriting = 0;
/**
* 读锁,允许同时读
*
* @throws InterruptedException 中断异常
*/
public synchronized void readLock() throws InterruptedException {
try {
waitReading++;
while (workWriting > 0)
this.wait();
workReading++;
} finally {
waitReading--;
}
}
/**
* 解除读锁
*/
public synchronized void readUnLock() {
workReading--;
this.notifyAll();
}
/**
* 写锁,允许单一写
* @throws InterruptedException
*/
public synchronized void writLock() throws InterruptedException {
try {
waitWriting++;
while (workWriting > 0 || workReading > 0)
this.wait();
workWriting++;
} finally {
waitWriting--;
}
}
/**
* 解除写锁
*/
public synchronized void writUnLock() {
workWriting--;
this.notifyAll();
}
}
共享数据
public class ReadWriteShareData {
private final ReadWriteLock LOCK = new ReadWriteLock();
private int data = 0;
public void read() throws InterruptedException {
LOCK.readLock();
IntStream.rangeClosed(1, 10).forEach(i -> {
if (i == 10)
System.out.println();
else
System.out.print(data);
});
Thread.sleep(100);
LOCK.readUnLock();
}
public void write(int data) throws InterruptedException {
LOCK.writLock();
this.data=data;
LOCK.writUnLock();
}
}
读线程
public class UsingReadWriteLockReader <T> extends Thread {
private T data = null;
public UsingReadWriteLockReader(T data) {
this.data = data;
}
@Override
public void run() {
try {
while (true){
if(data.getClass()==ReadWriteShareData.class)
((ReadWriteShareData)data).read();
else if (data.getClass()==ReadWriteShareDataWriteFirst.class)
((ReadWriteShareDataWriteFirst)data).read();
else
System.out.println("error");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
写线程
public class UsingReadWriteLockWriter<T> extends Thread {
private T data = null;
private int writeData = 0;
public UsingReadWriteLockWriter(T data, int writeData) {
this.data = data;
this.writeData = writeData;
}
@Override
public void run() {
try {
while (true)
if(data.getClass()==ReadWriteShareData.class)
((ReadWriteShareData)data).write(writeData);
else if (data.getClass()==ReadWriteShareDataWriteFirst.class)
((ReadWriteShareDataWriteFirst)data).write(writeData);
else
System.out.println("error");
} catch (InterruptedException e) {
// e.printStackTrace();
}
}
}
读写锁的缺陷
缺陷的分析
由于读线程的有限级别较高,造成了写线程总是无法进行线程的修改,这将会导致读线程一直占用文件,而无法进行写,导致无法更新数据。
缺陷的解决
为了解决以上问题,可为写线程添加一个优先标识位,这样即可解决相关问题。
解决的实现代码
读写锁改进
/**
* 读写锁核心,添加写优先
*/
public class ReadWriteLockWriteFirst {
/**
* 等待读的进程数
*/
private int waitReading = 0;
/**
* 工作读的进程数
*/
private int workReading = 0;
/**
* 等待写的进程数
*/
private int waitWriting = 0;
/**
* 工作写的进程数(最多只有一个)
*/
private int workWriting = 0;
/**
* 读写优先级
*/
private boolean writerFirst=false;
public ReadWriteLockWriteFirst() {
this(true);
}
public ReadWriteLockWriteFirst(boolean writerFirst) {
this.writerFirst = writerFirst;
}
/**
* 读锁,允许同时读
*
* @throws InterruptedException 中断异常
*/
public synchronized void readLock() throws InterruptedException {
try {
waitReading++;
//写锁修改点
while (workWriting > 0||(writerFirst&&waitWriting>0))
this.wait();
workReading++;
} finally {
waitReading--;
}
}
/**
* 解除读锁
*/
public synchronized void readUnLock() {
workReading--;
this.notifyAll();
}
/**
* 写锁,允许单一写
* @throws InterruptedException
*/
public synchronized void writLock() throws InterruptedException {
try {
waitWriting++;
while (workWriting > 0 || workReading > 0)
this.wait();
workWriting++;
} finally {
waitWriting--;
}
}
/**
* 解除写锁
*/
public synchronized void writUnLock() {
workWriting--;
this.notifyAll();
}
}