【详解】Java并发之读写锁分离设计模式
分析
读写锁最重要的需求是:多个线程如果都是在读取数据,如果依然采用加锁的方式,会严重影响效率。所以需要对读写锁进行分离
这种方式适用于:读取的操作比较多
需要考虑以下冲突,否则会出现数据不一致的情况
冲突 | 策略 |
---|---|
读 — 读 | 并行化 |
读 — 写 | 串行化 |
写 — 写 | 串行化 |
读写锁的设计思想就是避免冲突
编码
首先设计的读写锁
- 在获取读锁的时候,需要看看
是否存在正在写的线程
- 在获取写锁的时候,需要看看
是否存在正在写的线程
或者正在读的线程
- 释放锁的就是在完成后,
唤醒正在等待的全部线程
public class ReadWriteLock {
private int readingReaders = 0;//当前有几个线程正在读
private int waitingReaders = 0;//有几个线程想读,但是读取不了
private int writeingWriters = 0;//正在写的线程数,最多一个
private int waitingWriters = 0;//等待写的线程数
/** * 读锁只需要关心是否有没有人在写 */
public synchronized void readLock() {
this.waitingReaders++;
try {
//如果此时有线程正在写,则不允许读的操作,陷入阻塞
while (writeingWriters > 0) {
this.wait();
}
//此时没有线程正在写
this.readingReaders++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
waitingReaders--;
}
}
/** * 释放读的锁 */
public synchronized void readUnlock() {
this.readingReaders--;
this.notifyAll();
}
/** * 写锁 */
public synchronized void writeLock() {
this.waitingWriters++;
try {
//判断是否有线程正在读或者写
while (readingReaders>0 || writeingWriters>0){
this.wait();
}
writeingWriters++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
this.waitingWriters--;
}
}
/** * 释放写锁 */
public synchronized void wirteUnlock(){
this.writeingWriters--;
this.notifyAll();
}
}
验证
设计两个类分别去读取数据和写入数据,然后创建客户端,创建多个写线程和读线程
共享的数据
/** * 共享的数据 */
public class ShareData {
private final char [] buffer;
private final ReadWriteLock lock = new ReadWriteLock();
public ShareData(int size) {
this.buffer = new char[size];
for (int i = 0; i < size; i++) {
buffer[i] = '*';
}
}
/** * 写的操作 * @param c 写的字符 */
public void write(char c){
try {
lock.writeLock();
doWrite(c);
} finally {
lock.wirteUnlock();
}
}
private void doWrite(char c) {
for (int i = 0; i < buffer.length; i++) {
buffer[i] = c;
}
slowly(10);
}
public char[] read(){
try {
lock.readLock();
return doRead();
} finally {
lock.readUnlock();
}
}
/** * 读的操作,返回一个数据的副本 * @return */
private char[] doRead() {
char [] newBuf = new char[buffer.length];
for (int i = 0; i < buffer.length; i++) {
newBuf[i] = buffer[i];
}
slowly(50);
return newBuf;
}
/** * 休眠 * @param time 休眠时间 */
private void slowly(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
写的线程
public class WriteWorker extends Thread {
private final Random random = new Random(System.currentTimeMillis());
private final ShareData data;
private final String filer;
private int index = 0;
public WriteWorker(ShareData data, String filer) {
this.data = data;
this.filer = filer;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
char c = nextChar();
data.write(c);
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
}
}
/** * 获取一个字符,写到数据 * * @return */
private char nextChar() {
char c = filer.charAt(index);
index++;
if (index >= filer.length()) {
index = 0;
}
return c;
}
}
读取的线程
public class ReadWorker extends Thread {
private final Random random = new Random();
private final ShareData data;
public ReadWorker(ShareData data) {
this.data = data;
}
@Override
public void run() {
try {
while (!Thread.interrupted()){
char[] read = data.read();
System.out.println(Thread.currentThread().getName() + " reads " + String.valueOf(read));
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
}
}
}
客户端
public class Client {
public static void main(String[] args) throws InterruptedException {
ShareData data = new ShareData(5);
new ReadWorker(data).start();
new ReadWorker(data).start();
new ReadWorker(data).start();
new ReadWorker(data).start();
new ReadWorker(data).start();
new WriteWorker(data,"uiiakihlkbjhao465117").start();
new WriteWorker(data,"uiiakihlkbjhao465117").start();
Thread.sleep(10_000);
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
threadGroup.interrupt();
}
}
结果:
Thread-1 reads *****
Thread-2 reads *****
Thread-0 reads *****
Thread-3 reads *****
Thread-4 reads *****
Thread-3 reads uuuuu
Thread-4 reads uuuuu
Thread-3 reads uuuuu
Thread-0 reads uuuuu
Thread-3 reads uuuuu
Thread-2 reads iiiii
Thread-1 reads iiiii
Thread-3 reads kkkkk
Thread-1 reads kkkkk
Thread-4 reads kkkkk