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
结论:使用读写锁和重入锁进行写操作的速率大致相同。
在读多写少的场合,使用读写锁可以分离读操作和写操作,使所有读操作间
真正的并行。
使用场景:当线程使用读写操作共享数据时,使用读写锁,可以减少读线程的等待
时间提高系统的并发能力。