今天小组组织了一次java性能培训,提到了读写锁,以前没有关注过这个东西,于是培训结束后写个程序探索一下读写锁的使用方式。
final ReadWriteLock lock = new ReentrantReadWriteLock();
ReadWriteLock具有两把锁,lock.readLock()和lock.writeLock(),特性总结如下:
1、readLock与readLock之间不是互斥的,可以并发执行。实验代码如下:
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Main { public static void main(String[] args) { final ReadWriteLock lock = new ReentrantReadWriteLock(); new Thread(new Runnable() {
@Override public void run() {
lock.readLock().lock(); System.out.println(Thread.currentThread().getName() + ":Sleep begin."); // 休眠5秒 sleepInSeconds(5); System.out.println(Thread.currentThread().getName()+ ":Sleep complete."); lock.readLock().unlock();
} }, "Thread-1").start();
//此处休眠1秒,确保上面的代码得以先执行 sleepInSeconds(1);
new Thread(new Runnable() {
@Override public void run() { lock.readLock().lock(); System.out.println(Thread.currentThread().getName()+ ":Sleep begin."); // 休眠2秒 sleepInSeconds(2); System.out.println(Thread.currentThread().getName() + ":Sleep complete."); lock.readLock().unlock(); } }, "Thread-2").start(); }
/** * 休眠一定时间,单位5秒 * @param seconds 要休眠的秒数 */ private static void sleepInSeconds(int seconds) { try { Thread.sleep(seconds * 1000); } catch (InterruptedException e) { e.printStackTrace(); } }
}
|
Thread-1:Sleep begin. Thread-2:Sleep begin. Thread-2:Sleep complete. Thread-1:Sleep complete.
|
根据输出可以看出,虽然Thread-1已经调用了,lock.readLock().lock(),但是Thread-2还是在Thread-1调用lock.readLock().unlock()之前就执行打印了,说明两个readLock之间是互不影响的。
2、readLock和writeLock是互斥的,实验代码如下:
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Main { public static void main(String[] args) { final ReadWriteLock lock = new ReentrantReadWriteLock(); new Thread(new Runnable() {
@Override public void run() {
lock.readLock().lock(); System.out.println(Thread.currentThread().getName() + ":Sleep begin."); // 休眠5秒 sleepInSeconds(5); System.out.println(Thread.currentThread().getName()+ ":Sleep complete."); lock.readLock().unlock();
} }, "Thread-1").start();
//此处休眠1秒,确保上面的代码得以先执行 sleepInSeconds(1);
new Thread(new Runnable() {
@Override public void run() { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+ ":Sleep begin."); // 休眠2秒 sleepInSeconds(2); System.out.println(Thread.currentThread().getName() + ":Sleep complete."); lock.writeLock().unlock(); } }, "Thread-2").start(); }
/** * 休眠一定时间,单位5秒 * @param seconds 要休眠的秒数 */ private static void sleepInSeconds(int seconds) { try { Thread.sleep(seconds * 1000); } catch (InterruptedException e) { e.printStackTrace(); } }
}
|
Thread-1:Sleep begin. Thread-1:Sleep complete.
Thread-2:Sleep begin. Thread-2:Sleep complete.
|
3、writeLock与writeLock之间是互斥的,实验代码如下
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Main { public static void main(String[] args) { final ReadWriteLock lock = new ReentrantReadWriteLock(); new Thread(new Runnable() {
@Override public void run() {
lock.writeLock().lock(); System.out.println(Thread.currentThread().getName() + ":Sleep begin."); // 休眠5秒 sleepInSeconds(5); System.out.println(Thread.currentThread().getName()+ ":Sleep complete."); lock.writeLock().unlock();
} }, "Thread-1").start();
//此处休眠1秒,确保上面的代码得以先执行 sleepInSeconds(1);
new Thread(new Runnable() {
@Override public void run() { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+ ":Sleep begin."); // 休眠2秒 sleepInSeconds(2); System.out.println(Thread.currentThread().getName() + ":Sleep complete."); lock.writeLock().unlock(); } }, "Thread-2").start(); }
/** * 休眠一定时间,单位5秒 * @param seconds 要休眠的秒数 */ private static void sleepInSeconds(int seconds) { try { Thread.sleep(seconds * 1000); } catch (InterruptedException e) { e.printStackTrace(); } }
}
|
Thread-1:Sleep begin. Thread-1:Sleep complete.
Thread-2:Sleep begin. Thread-2:Sleep complete.
|
由于readLock与writeLock是互斥的,而readLock与readLock之间是非互斥的,产生了一种猜想,假设有这么一种场景:
Thread-1在第0秒的时候执行了readLock().lock()
Thread-1在第5秒的时候执行了readLock().unlock()
Thread-2在第2秒的时候执行了writeLock().lock()
Thread-2在第7秒的时候执行了writeLock().unlock()
Thread-3在第4秒的时候执行了writeLock().lock()
Thread-3在第9秒的时候执行了writeLock().unlock()
由于Thread-2的writeLock与Thread-1的readLock是互斥的,所以在第5秒之前Thread-2的writeLock之间的代码都不会执行,但Thread-1和Thread-3都是读锁,会不会出现Thread-3的readLock之间的代码先于Thread-2的writeLock之间的代码执行的情况,做个实验便知
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Main { private static long beginTime; public static void main(String[] args) { final ReadWriteLock lock = new ReentrantReadWriteLock(); beginTime = System.currentTimeMillis(); new Thread(new Runnable() {
@Override public void run() { System.out.println(Thread.currentThread().getName()+ " begin running."+getTime()); lock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+ ":Sleep begin."+getTime()); // 休眠5秒 sleepInSeconds(5); System.out.println(Thread.currentThread().getName()+ ":Sleep complete."+getTime()); lock.writeLock().unlock();
} }, "Thread-1").start(); //此处休眠1秒,确保上面的代码得以先执行 sleepInSeconds(2);
new Thread(new Runnable() {
@Override public void run() { System.out.println(Thread.currentThread().getName()+ " begin running."+getTime()); lock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+ ":Sleep begin."+getTime()); // 休眠2秒 sleepInSeconds(5); System.out.println(Thread.currentThread().getName()+ ":Sleep complete."+getTime()); lock.writeLock().unlock(); } }, "Thread-2").start(); //此处休眠1秒,确保上面的代码得以先执行 sleepInSeconds(2);
new Thread(new Runnable() {
@Override public void run() { System.out.println(Thread.currentThread().getName()+ " begin running."+getTime()); lock.readLock().lock(); System.out.println(Thread.currentThread().getName()+ ":Sleep begin."+getTime()); // 休眠2秒 sleepInSeconds(5); System.out.println(Thread.currentThread().getName()+ ":Sleep complete."+getTime()); lock.readLock().unlock(); } }, "Thread-3").start(); }
/** * 休眠一定时间,单位5秒 * @param seconds 要休眠的秒数 */ private static void sleepInSeconds(int seconds) { try { Thread.sleep(seconds * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 获取时间字符串 * @return */ private static String getTime() { return " Time:"+(System.currentTimeMillis() - beginTime)/1000; }
}
|
Thread-1 begin running. Time:0 Thread-1:Sleep begin. Time:0 Thread-2 begin running. Time:2 Thread-3 begin running. Time:4 Thread-1:Sleep complete. Time:5 Thread-2:Sleep begin. Time:5 Thread-2:Sleep complete. Time:10 Thread-3:Sleep begin. Time:10 Thread-3:Sleep complete. Time:15
|
从实验结果来看,Thread-1,Thread-2,Thread-3的代码已经按照原先设计在分别在0秒,2秒,4秒的时刻被调用,但是其lock部分的代码却是:Thread-1的lock代码先执行,Thread-1执行完了才执行Thread-2的lock代码,Thread-2执行完了才执行Thread-3的lock代码。
之前猜想的Thread-3的lock代码先于Thread-2的lock代码执行的情况没有出现,也就是说虽然当前正在执行的代码是readLock的代码,但如果存在一个writeLock排队,那么其它readLock是无法与当前正在执行的readLock并行执行的。
欢迎拍砖