首先我们对读写锁做一个概述:
假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写,也就是说:读-读能共存,读-写不能共存,写-写不能共存。这就需要一个读/写锁来解决这个问题。
阿里巴巴四面最后的问题就涉及到了读写锁的实现。
问:对JAVA中的读写锁熟悉吗?
我:还可以...
问:简单介绍一下...
我:这是JAVA中的集合类,ReentrantReadWriteLock,分别处理读与写的操作,读读可以共存,读写不能共存,写写不能共存...
问:你自己实现一个读写锁
我:用synchronized,用一个队列...
问:不能那么麻烦,就用两个int值
我:两个int值,那一定是一个标记读次数,一个标记写次数啊,于是想了一会我想出了以下答案:
问:你这个可以是可以,但是如果读操作大量的话,写操作会产生饥饿等待...如何改进?
我想了想,是哦,然后脑子就短路了,挣扎,再挣扎...
我:给写加上优先级
问:不可以,就两个int
我:读次数设置一个阈值,超出主动释放
问:不可以,就两个int
我一脸懵逼...脑补画面
问:提醒你一下,lockwrite那里改一下
面试官发音不标准,我听了个lockread,想了半天,实在不知道怎么改,随便说了一个
问:我让你改lockwrite...
我:额,我看看...
又是漫长的等待...
我:没想出来...
问:把你那个条件拆开,让写占个坑...
我恍然大悟,我去...
然后他大概说了一遍,就草草结束了,郁闷中...
应该改成:
回来之后,大概看了一下,把整个程序写了一下:
package com.darrenchan.lock; /** * 用两个int变量实现读写锁 * @author Think * */ public class MyReadWriteLock { private int readcount = 0; private int writecount = 0; public void lockread() throws InterruptedException{ while(writecount > 0){ synchronized(this){ wait(); } } readcount++; //进行读取操作 System.out.println("读操作"); } public void unlockread(){ readcount--; synchronized(this){ notifyAll(); } } public void lockwrite() throws InterruptedException{ while(writecount > 0){ synchronized(this){ wait(); } } //之所以在这里先++,是先占一个坑,避免读操作太多,从而产生写的饥饿等待 writecount++; while(readcount > 0){ synchronized(this){ wait(); } } //进行写入操作 System.out.println("写操作"); } public void unlockwrite(){ writecount--; synchronized(this){ notifyAll(); } } public static void main(String[] args) throws InterruptedException { MyReadWriteLock readWriteLock = new MyReadWriteLock(); for(int i = 0; i < 2; i++){ Thread2 thread2 = new Thread2(i, readWriteLock); thread2.start(); } for (int i = 0; i < 10; i++) { Thread1 thread1 = new Thread1(i, readWriteLock); thread1.start(); } } } class Thread1 extends Thread{ public int i; public MyReadWriteLock readWriteLock; public Thread1(int i, MyReadWriteLock readWriteLock) { this.i = i; this.readWriteLock = readWriteLock; } @Override public void run() { try { readWriteLock.lockread(); Thread.sleep(1000);//模拟耗时 System.out.println("第"+i+"个读任务"); } catch (InterruptedException e) { e.printStackTrace(); } finally { readWriteLock.unlockread(); } } } class Thread2 extends Thread{ public int i; public MyReadWriteLock readWriteLock; public Thread2(int i, MyReadWriteLock readWriteLock) { this.i = i; this.readWriteLock = readWriteLock; } @Override public void run() { try { readWriteLock.lockwrite(); Thread.sleep(1000); System.out.println("第"+i+"个写任务"); } catch (InterruptedException e) { e.printStackTrace(); } finally { readWriteLock.unlockwrite(); } } }
执行结果:
写操作
第0个写任务
读操作
读操作
读操作
读操作
读操作
读操作
读操作
读操作
读操作
读操作
第9个读任务
第3个读任务
第1个读任务
第8个读任务
第2个读任务
第0个读任务
第7个读任务
第4个读任务
第5个读任务
第6个读任务
写操作
第1个写任务
当然这是简单的,还可以进一步加深,可以参考博客:http://ifeve.com/read-write-locks/#simple
注:以上代码在++和--的时候仍然会产生并发异常,建议用AtomicInteger类型,在硬件上保证++和--操作不会出现并发异常。