带你实现一个你上你也行的读写锁

1,140 阅读3分钟

前言

锁是啥我就不多BB了,各位在学习JUC前先简单实现一个自己的读写锁岂不美哉。

正文

读写锁
/**
* 读写锁
*/
public class ReadWriteLock {
private int readingReaders = 0; //正在读的线程数
private int waitingReaders = 0; //正在等待锁的读线程数
private int writingWriters = 0; //正在写的线程数
private int waitingWriters = 0; //正在等待锁的写线程数
private boolean readerFirst = true; //写者优先
/**
* 无参构造,默认读者优先
*/
public ReadWriteLock() {
this(true);
}

public ReadWriteLock(boolean preferReader) {
this.readerFirst = preferReader;
}

/**
* 读锁
*/
public synchronized void readLock() throws InterruptedException {
//有读线程需要来获取锁,读等待线程数量加1.
this.waitingReaders++;
try {
//如果有读线程正在写,则阻塞。不阻塞读,到达读读无锁的目的。
while (writingWriters > 0) {
this.wait();
}
//没有写线程正在写,则可以进行读了,并将正在读线程数量++。
this.readingReaders++;
}finally {
//等待读线程数量--
this.waitingReaders--;
}
}

/**
* 读解锁
*/
public synchronized void readUnlock() {
//读完成后,正在读线程数量--
this.readingReaders--;
//唤醒所有被阻塞的线程,能被读解锁唤醒的阻塞线程一定是写线程。
this.notifyAll();
}

/**
* 写锁
*/
public synchronized void writeLock() throws InterruptedException {
//有写线程需要来获取锁,写等待线程数量加1.
this.waitingWriters++;
try {
//如果有正在写的线程 或 有正在读的线程 或 有等待的读线程(读者优先),则当前写线程阻塞。
while (writingWriters > 0 || readingReaders > 0 || (readerFirst && waitingReaders > 0)) {
this.wait();
}
//如果无,则可以开始进行写,正在写线程数量++
this.writingWriters++;
} finally {
//等待读线程数量--
this.waitingWriters--;
}
}

/**
* 写解锁
*/
public synchronized void writeUnlock() {
//写完成后,正在写线程数量--
this.writingWriters--;
//唤醒所有被阻塞的线程,读写皆有可能。
this.notifyAll();
}

public int getWaitingWriters(){
return waitingWriters;
}

public int getWaitingReaders(){
return waitingReaders;
}
}

上文代码通过条件判断统计参数以及阻塞的方式,实现了一个简单的“读写”、“写写”互斥的锁,但是“读读”不互斥。

如果写线程获取锁之前有正在等待读(之前有人正在写,阻塞住了),那么写线程阻塞进行等待,优先让给正在等待读的线程。

注:如果想更改为写者优先的话,可以更改代码,增加标志位,或者是标志位复用的方式,并且更改循环里判断条件即可。

共享资源

/*
* 共享资源
**/
public class SharedData {

private String value;

private final ReadWriteLock lock = new ReadWriteLock();

public SharedData() {
value = "";
}

public String read() throws InterruptedException {
try {
lock.readLock();
System.out.println("正在写等待的线程数量 :"+lock.getWaitingWriters());
System.out.println("正在读等待的线程数量 :"+lock.getWaitingReaders());
System.out.println(Thread.currentThread().getName() + " 读到了: " + this.value);
System.out.println("------------------------------------------------");
sleep(100);
return this.value;
} finally {
lock.readUnlock();
}
}

public void write(String s) throws InterruptedException {
try {
lock.writeLock();
System.out.println("正在写等待的线程数量:"+lock.getWaitingReaders());
System.out.println("正在读等待的线程数量 :"+lock.getWaitingReaders());
System.out.println(Thread.currentThread().getName() + " 写了: " + s);
System.out.println("------------------------------------------------");
sleep(200);
this.value = s;
} finally {
lock.writeUnlock();
}
}

private void sleep(int ms) throws InterruptedException {
Thread.sleep(ms);
}
}

模拟读和写的工作线程

读工作线程
* 模拟读线程
*/
public class ReaderWorker extends Thread {

private static final Random random = new Random(System.currentTimeMillis());

//共享资源
private final SharedData data;

public ReaderWorker(SharedData data) {
this.data = data;
}

@Override
public void run() {
try {
while (true) {
String s = data.read();
sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"被中断了~");
return;
}
}
}
写工作线程
 * 模拟写线程
*/
public class WriterWorker extends Thread {

private static final Random random = new Random(System.currentTimeMillis());

private final SharedData data;

private String s;

public WriterWorker(SharedData data, String s) {
this.data = data;
this.s = s;
}

@Override
public void run() {
try {
while (true) {
data.write(s);
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
国际惯例主线程psvm
 * 读写锁test
*/
public class ReadWritLockClient {
public static void main(String[] args) {
final SharedData sharedData = new SharedData();
new ReaderWorker(sharedData).start();
new ReaderWorker(sharedData).start();
new WriterWorker(sharedData, "ABC").start();
new WriterWorker(sharedData, "DEF").start();
new WriterWorker(sharedData, "GHI").start();
new WriterWorker(sharedData, "JKL").start();
new WriterWorker(sharedData, "LMN").start();
}
}

模拟若干个读写线程进行共享资源竞争

验证

不会出现“读写”、“写写”的情况~ 完事

本文使用 mdnice 排版