一,Read-Write Lock模式
在Read-Write Lock模式中,读取操作和写入操作是分开考虑的。在执行读取操作之前,线程必须获取用于读取的锁。
在执行写入操作之前,线程必须获取用于写入的锁。所以:
当一个线程在读取时,其他线程可以读取,但是不可以写入。
当一个线程正在写入时,其他线程不可以读取或写入。
因为执行互斥处理会降低程序的性能,但是如果把写入的互斥处理和读取的互斥处理分开来考虑,就可以提高系统性能
二,示例程序
public final class ReadWriteLock { private int readingReaders = 0;//实际正在读取中的线程个数 private int waitingWriters = 0;//正在等待写入的线程个数 private int writingWriters = 0;//实际正在写入的线程个数 private boolean preferWriter = true;//若写入优先,则为true public synchronized void readLock()throws InterruptedException{ while (writingWriters > 0 || (preferWriter && waitingWriters > 0)){ wait(); } readingReaders++;//正在读取的线程个数加1 } public synchronized void readUnlock(){ readingReaders--;//正在读取的线程个数减1 preferWriter = true; notifyAll(); } public synchronized void writeLock()throws InterruptedException{ waitingWriters++;//等待写入的线程加1 try { while (readingReaders > 0 || writingWriters > 0){ wait(); } }finally { waitingWriters--;//等待写入的线程减1 } writingWriters++;//正在写入的线程加1 } public synchronized void writeUnlock(){ writingWriters--;//正在写入的线程减1 preferWriter = false; notifyAll(); } }
public class Data { private final char[] buffer; public final ReadWriteLock lock = new ReadWriteLock(); public Data(int size){ this.buffer = new char[size]; for (int i = 0; i < buffer.length; i++) { buffer[i] = '*'; } } public char[] read()throws InterruptedException{ lock.readLock(); try { return doRead(); }finally { lock.readUnlock(); } } /** * 实际的读取操作,创建一个新的char数组newbuf,来复制buffer里的内容,并返回newbuf * @return */ private char[] doRead(){ char[] newbuf = new char[buffer.length]; for (int i = 0; i < buffer.length; i++) { newbuf[i] = buffer[i]; } slowly(); return newbuf; } public void write(char c)throws InterruptedException{ lock.writeLock(); try { doWrite(c); }finally { lock.writeUnlock(); } } /** * 实际的写入操作,每读取一个字符,就执行一次耗时操作。写入操作时间比读取操作时间长 * @param c */ public void doWrite(char c){ for (int i = 0; i < buffer.length; i++) { buffer[i] = c; slowly(); } } /** * 耗时操作,模拟处理业务逻辑 */ public void slowly(){ try { Thread.sleep(50); }catch (InterruptedException e){ } } }
public class ReaderThread extends Thread { private final Data data; public ReaderThread(Data data){ this.data = data; } @Override public void run() { try { while (true){ char[] readbuf = data.read(); System.out.println(Thread.currentThread().getName()+" reads "+ String.valueOf(readbuf)); } }catch (InterruptedException e){ } } }
public class WriterThread extends Thread { private static final Random random = new Random(); private final Data data; private final String filler; private int index = 0; public WriterThread(Data data,String filler){ this.data = data; this.filler = filler; } @Override public void run() { try { while (true){ char c = nextchar(); data.write(c); Thread.sleep(random.nextInt(3000)); } }catch (InterruptedException e){ } } private char nextchar(){ char c = filler.charAt(index); index++; if (index >= filler.length()){ index = 0; } return c; } }
public class Test { public static void main(String[] args) { Data data = new Data(10); for (int i = 0; i < 6; i++) { new ReaderThread(data).start(); } new WriterThread(data,"ABCDEFGHIJKLMNOPQRSTUVWXYZ").start(); new WriterThread(data,"abcdefghijklmnopqrstuvwxyz").start(); } }
三,适用场景
1.读取操作频繁
2.读取频率比写入频率高
四,锁的含义
1.synchronized可以用于获取实例的锁,Java的每一个实例都持有一个锁,但同一个锁不能被两个以上的线程同时获取,
这种结构是Java编程规范规定的,java虚拟机也是这样实现的。这就是java提供的物理锁。
2.本章的 用于读取的锁 和 用于写入的锁,是开发人员自己实现的一种结构。这种锁是逻辑锁,我们可以通过修改ReadWriteLock类来改变锁的运行
ReadWriteLock类提供了用于 读取的锁 和 用于写入的锁 的这两个逻辑锁。但是用于实现这两个逻辑锁的物理锁只有一个,就是ReadWriteLock实例持有的锁
五,java.util.concurrent.locks.ReadWriteLock
java并发包提供了读写锁。
public class Data { private final char[] buffer; private final ReadWriteLock lock = new ReentrantReadWriteLock(true); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); public Data(int size){ this.buffer = new char[size]; for (int i = 0; i < buffer.length; i++) { buffer[i] = '*'; } } public char[] read()throws InterruptedException{ readLock.lock(); try { return doRead(); }finally { readLock.unlock(); } } /** * 实际的读取操作,创建一个新的char数组newbuf,来复制buffer里的内容,并返回newbuf * @return */ private char[] doRead(){ char[] newbuf = new char[buffer.length]; for (int i = 0; i < buffer.length; i++) { newbuf[i] = buffer[i]; } slowly(); return newbuf; } public void write(char c)throws InterruptedException{ writeLock.lock(); try { doWrite(c); }finally { writeLock.unlock(); } } /** * 实际的写入操作,每读取一个字符,就执行一次耗时操作。写入操作时间比读取操作时间长 * @param c */ public void doWrite(char c){ for (int i = 0; i < buffer.length; i++) { buffer[i] = c; slowly(); } } /** * 耗时操作,模拟处理业务逻辑 */ public void slowly(){ try { Thread.sleep(50); }catch (InterruptedException e){ } } }