日常开发中,有这样的场景,某些数据读的次数远超于写的次数,但是在多线程环境下读写并不能同时进行。读是可以并行处理的,但是写却不能。
读 | 写 | |
---|---|---|
读 | ✅ | ❌ |
写 | ❌ | ❌ |
只有读读是可以并行的,别的必须做到读写分离。
下面将演示读写锁模式,读的时候放开,但是写的时候需要添加锁机制,保证线程安全。
// 读写锁
public class ReadWriteLock {
private int readingReaders = 0;
private int waitingReaders = 0;
private int writingWriters = 0;
private int waitingWriters = 0;
//偏爱写锁、否则可能读拿到锁一直不放开
private boolean preferWriter = true;
public ReadWriteLock() {
this(true);
}
public ReadWriteLock(Boolean preferWriter) {
this.preferWriter = preferWriter;
}
public synchronized void readLock() throws InterruptedException {
//进来就先加一个 reader
this.waitingReaders++;
try {
while (writingWriters > 0 || (preferWriter && waitingWriters > 0)) {
this.wait();
}
//说明在reader
this.readingReaders++;
} finally {
//最后减一个 reader
this.waitingReaders--;
}
}
public synchronized void readerUnlock() {
//可能百自己减掉 、但是没关系
this.readingReaders--;
this.notifyAll();
}
public synchronized void writerLock() throws InterruptedException {
this.waitingWriters++;
try {
while (readingReaders > 0 || writingWriters > 0) {
this.wait();
}
this.writingWriters++;
} finally {
this.waitingWriters--;
}
}
public synchronized void writerUnlock() {
this.writingWriters--;
this.notifyAll();
}
}
// 读线程
public class ReaderWorker extends Thread {
private final SharedData data;
public ReaderWorker(SharedData data) {
this.data = data;
}
@Override
public void run() {
try {
while (true) {
char[] readerBuff = data.read();
System.out.println(Thread.currentThread().getName()+" reads "+ String.valueOf(readerBuff));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 写线程
public class WriterWorker extends Thread {
private static final Random random = new Random(System.currentTimeMillis());
private final SharedData data;
private final String filer;
private int index = 0;
public WriterWorker(SharedData data, String filer) {
this.data = data;
this.filer = filer;
}
@Override
public void run() {
try {
while (true) {
char c = nextChar();
data.writer(c);
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private char nextChar() {
char c = filer.charAt(index);
index++;
if (index >= filer.length()) {
index = 0;
}
return c;
}
}
// 共享数据
public class SharedData {
//共享的数据是一个数组 buffer
private final char[] buffer;
private final ReadWriteLock lock = new ReadWriteLock();
public SharedData(int size) {
this.buffer = new char[size];
for (int i = 0; i < size; i++) {
buffer[i] = '*';
}
}
public char[] read() throws InterruptedException {
try {
lock.readLock();
return this.doRead();
} finally {
lock.readerUnlock();
}
}
public void writer(char c) throws InterruptedException {
try {
lock.writerLock();
this.doWriter(c);
} finally {
lock.writerUnlock();
}
}
private void doWriter(char c) {
for (int i = 0; i < buffer.length; i++) {
buffer[i] = c;
slowly(10);
}
}
private char[] doRead() {
char[] newBuff = new char[buffer.length];
for (int i = 0; i < newBuff.length; i++) {
newBuff[i] = buffer[i];
}
slowly(50);
return newBuff;
}
private void slowly(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// test
public class ReaderWriterClient {
public static void main(String[] args) {
final SharedData sharedData = new SharedData(10);
new ReaderWorker(sharedData).start();
new ReaderWorker(sharedData).start();
new ReaderWorker(sharedData).start();
new ReaderWorker(sharedData).start();
new ReaderWorker(sharedData).start();
new WriterWorker(sharedData, "qweradsfdgdfbdf").start();
new WriterWorker(sharedData, "QWEERTYUI").start();
}
}