锁是啥我就不多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 排版