ReadWriteLock: 读写锁
ReadWriteLock:
JDK1.5提供的读写分离锁,采用读写锁分离可以有效帮助减少锁竞争。
特点:
1).使用读写锁。当线程只进行读操作时,可以允许多个线程同时读
2).写写操作,读写操作间依然需要相互等待和持有锁。
一).使用读写锁与使用重入锁进行读读写操作
开启200个线程,测试读写锁和重入锁的读效率。
使用重入锁进行读写操作:ReentrantLock_Rw
import java.util.concurrent.locks.ReentrantLock; /** * 使用重入锁进行读写操作 * 线程的读写使用同一把锁。 */ public class ReentrantLock_RW { private static ReentrantLock lock = new ReentrantLock(); private static int value ; //读操作 public Object handleRead() throws InterruptedException { try { //获取锁 lock.lock(); //模拟读操作,读操作耗时越多,读写锁的优势越明显 Thread.sleep(1); return value; }finally { lock.unlock(); } } /** * 写操作 */ public void handleWrite(int i) throws InterruptedException { try { lock.lock(); //模拟写操作 Thread.sleep(1); value = i; }finally { lock.unlock(); } } }
使用ReadWriteLock进行读写操作: ReadWriteLock_RW
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 使用ReadWriteLock进行读写操作 */ public class ReadWriteLock_RW { private static int value; private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** * 读取锁 */ private static Lock readLock = readWriteLock.readLock(); /** * 写入锁 */ private static Lock writeLock = readWriteLock.writeLock(); /** * 读操作 */ public Object handleRead() throws InterruptedException { try { readLock.lock(); Thread.sleep(1); return value; }finally { //释放锁 readLock.unlock(); } } /** * 写操作 */ public void handleWrite(int index) throws InterruptedException { try{ //获取锁 writeLock.lock(); Thread.sleep(1); value = index; }finally { //释放锁 writeLock.unlock(); } } }
二)、测试多线程运行时间的知识储备
参考:https://www.cnblogs.com/jack-xsh/p/8615644.html
怎么测试多线程的运行时间?
//指定要开启的线程数 final static CountdownLatch countdownLatch = new CountdownLatch(200);
//每执行完一个线程,countdownLatch的线程数减一。 countdownLatch.countdown();
//挂起主线程,当cuntdown()的线程数为0,恢复主线程。 countdownLatch.await();
三)、使用重入锁进行读操作性能测试
重入锁读操作线程:ReentrantLockReadThread
** * 比较重入锁与读写锁读数据的性能 */ public class ReentrantLockReadThread implements Runnable{ //闭锁,线程计时工具 private CountDownLatch countDownLatch; private ReentrantLock_RW reentrantLockRw; public ReentrantLockReadThread(ReentrantLock_RW reentrantLockRw, CountDownLatch countDownLatch) { this.reentrantLockRw = reentrantLockRw; this.countDownLatch = countDownLatch; } @Override public void run() { try { reentrantLockRw.handleRead(); } catch (InterruptedException e) { e.printStackTrace(); }finally { //线程执行完将线程挂起 countDownLatch.countDown(); } } }
重入锁读线程效率:ReentrantLockReadThreadTest
public class ReentrantLockReadThreadTest { //程序计计时器,用于计算多线程的执行时间 final static CountDownLatch countDownLatch = new CountDownLatch(200); //其实开启200个线程读取lock中值的速率 public static void main(String[] args) throws InterruptedException { ReentrantLock_RW reentrantLockRw = new ReentrantLock_RW(); ReentrantLockReadThread readThread = new ReentrantLockReadThread(reentrantLockRw, countDownLatch); long time = System.currentTimeMillis(); for(int i = 0; i < 200; i++){ Thread t = new Thread(readThread); t.start(); } //将主线程挂起 countDownLatch.await(); System.out.println(System.currentTimeMillis() - time); } }
结果:
2194
四)、使用读写锁进行读操作的性能测试
读写锁读操作线程:ReadLockThread
import java.util.concurrent.CountDownLatch; /** * 读入锁线程 */ public class ReadLockThread implements Runnable{ private ReadWriteLock_RW readWriteLockrw; private CountDownLatch countDownLatch; public ReadLockThread(ReadWriteLock_RW readWriteLockrw, CountDownLatch countDownLatch) { this.readWriteLockrw = readWriteLockrw; this.countDownLatch = countDownLatch; } @Override public void run() { try { readWriteLockrw.handleRead(); } catch (InterruptedException e) { e.printStackTrace(); }finally { //线程执行完将该线程挂起 countDownLatch.countDown(); } } }
读线程读写效率测试:ReadLockThreadTest
import java.util.concurrent.CountDownLatch; /** * 测试使用ReadLock的性能 */ public class ReadLockThreadTest { final static CountDownLatch countDownLatch = new CountDownLatch(200); public static void main(String[] args) throws InterruptedException { ReadWriteLock_RW readWriteLockRw = new ReadWriteLock_RW(); ReadLockThread readThread = new ReadLockThread(readWriteLockRw, countDownLatch); long time = System.currentTimeMillis(); for(int i = 0; i < 200; i++){ Thread t = new Thread(readThread); t.start(); } countDownLatch.await(); //一定要等到countDown()方法执行完毕后才使用,将主线程挂起 System.out.println(System.currentTimeMillis() - time); } }
结果:
26
结论:使用读写锁来对数据进行读取,效率远远高于重入锁。
五)、使用重入锁进行写操作的性能比较
重入锁写操作线程:ReentrantLockWriteThread
public class ReentrantLockWriteThread implements Runnable { private CountDownLatch countDownLatch; private ReentrantLock_RW reentrantLockRw; public ReentrantLockWriteThread(ReentrantLock_RW reentrantLockRw, CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; this.reentrantLockRw = reentrantLockRw; } @Override public void run() { try { reentrantLockRw.handleWrite(1); } catch (InterruptedException e) { e.printStackTrace(); }finally { //线程执行完,线程数减一 countDownLatch.countDown(); } } }
写线程效率测试:ReentrantLockWriteThreadTest
public class ReentrantLockWriteThreadTest { final static CountDownLatch countDownLatch = new CountDownLatch(200); public static void main(String[] args) throws InterruptedException { ReentrantLock_RW reentrantLockRw = new ReentrantLock_RW(); long start = System.currentTimeMillis(); for(int i = 0; i < 200; i++){ Thread t = new Thread(new ReentrantLockWriteThread(reentrantLockRw, countDownLatch)); t.start(); } countDownLatch.await(); System.out.println(System.currentTimeMillis() - start); } }
结果:
408
六)、使用读写锁进行写操作性能比较
读写锁写操作线程: WriteLockThread
public class WriteLockThread implements Runnable{ private ReadWriteLock_RW readWriteLockrw; private CountDownLatch countDownLatch; public WriteLockThread(ReadWriteLock_RW readWriteLockrw, CountDownLatch countDownLatch) { this.readWriteLockrw = readWriteLockrw; this.countDownLatch = countDownLatch; } @Override public void run() { try { readWriteLockrw.handleWrite(1); } catch (InterruptedException e) { e.printStackTrace(); }finally { //线程执行完将该线程挂起 countDownLatch.countDown(); } } }
写线程效率测试:WriteLockThreadTest
public class WriteLockThread implements Runnable{ private ReadWriteLock_RW readWriteLockrw; private CountDownLatch countDownLatch; public WriteLockThread(ReadWriteLock_RW readWriteLockrw, CountDownLatch countDownLatch) { this.readWriteLockrw = readWriteLockrw; this.countDownLatch = countDownLatch; } @Override public void run() { try { readWriteLockrw.handleWrite(1); } catch (InterruptedException e) { e.printStackTrace(); }finally { //线程执行完将该线程挂起 countDownLatch.countDown(); } } }
结果:
398
结论:使用读写锁和重入锁进行写操作的速率大致相同。
在读多写少的场合,使用读写锁可以分离读操作和写操作,使所有读操作间
真正的并行。
使用场景:当线程使用读写操作共享数据时,使用读写锁,可以减少读线程的等待
时间提高系统的并发能力。