java多线程之Lock介绍
java.util.concurrent包中有关于Lock锁的定义。它提供了ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,重入锁、读锁和写锁等。
ReentrantLock
公平锁和非公平锁
公平锁当线程请求锁时,会将其加入到请求队列中,
非公平锁就是当前线程不管有无请求队列,先去请求锁,如果请求不到,加入到队列末尾。
具体分析可以参考
http://www.jianshu.com/p/f7d05d06ef54
默认构造方法创建非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
Lock与synchronized的区别
- lock是显示的锁,调用lock()添加锁,必须调用unlock()释放锁。
- 使用lock.lock()调用线程的interrupt会中断线程,而synchronized锁调用interrupt不会中断线程。
实例来证明2的结论
我们知道synchronized的interrupt方法只是设置中断标志,并没有真正的中断线程。下面这个代码示例用于说明,
代码如下:
设计两个线程,一个read线程,一个write线程。
当read线程正在read的时候,write线程处于等待状态、
然后中断write线程,看会有什么情况发生
如果write中断,会打印 write end…
因为Reentrant对中断锁做出响应,会中断线程的执行。
public static void syncReaderWriterTest(){
MyReentrantLockBuffer mySyncBuffer = new MyReentrantLockBuffer();
final Thread reader = new Thread(new SyncReader(mySyncBuffer));
final Thread writer = new Thread(new SyncWriter(mySyncBuffer));
reader.start();
writer.start();
System.out.println(Thread.currentThread().getName() + " writer 不在等待了...");
writer.interrupt();
System.out.println(Thread.currentThread().getName() + " writer 已经中断, " + writer.isInterrupted());
}
/**
* 写操作,传入MySyncBuffer的构造方法,保证两个对象持有相同的对象锁
*/
static class SyncWriter implements Runnable{
private MyReentrantLockBuffer mySyncBuffer;
public SyncWriter(MyReentrantLockBuffer mySyncBuffer){
this.mySyncBuffer = mySyncBuffer;
}
public void run() {
try {
mySyncBuffer.write();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " write end." + Thread.currentThread().isInterrupted());
}
}
/**
* 读操作,传入MySyncBuffer的构造方法,保证两个对象持有相同的对象锁
*/
static class SyncReader implements Runnable{
private MyReentrantLockBuffer mySyncBuffer;
public SyncReader(MyReentrantLockBuffer mySyncBuffer){
this.mySyncBuffer = mySyncBuffer;
}
public void run() {
mySyncBuffer.read();
}
}
可重入性
锁的可重入性体现在两个方面
1. methodA和methodB拥有同一个对象锁,线程进入A方法中,在没有释放锁的前提下,可以直接调用methodB方法。
static class ReentrantDemoRunnable implements Runnable{
final ReentrantLock reentrantLock = new ReentrantLock();
public void methodA() {
System.out.println(" lock before methodAAAAAA ========" );
reentrantLock.lock();
try {
System.out.println(" run methodA and lock..." );
this.methodB();
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println(" run methodA end unlock..." );
reentrantLock.unlock();
}
System.out.println(" methodAAAAAA ======== end =====" );
}
public void methodB() {
System.out.println(" lock before methodBBBBBB ========" );
reentrantLock.lock();
try {
System.out.println(" run methodB and lock..." );
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println(" run methodB end unlock..." );
reentrantLock.unlock();
}
System.out.println(" methodBBBBBB ======== end =====" );
}
}
多线程调用methodA执行的顺序为:
lock before methodAAAAAA ========
run methodA and lock...
lock before methodBBBBBB ========
run methodB and lock...
run methodB end unlock...
methodBBBBBB ======== end =====
run methodA end unlock...
methodAAAAAA ======== end =====
- 在加锁的前提下,在子类方法中调用父类的方法。
static class Father{
final ReentrantLock lock = new ReentrantLock();
public void method(){
lock.lock();
try {
System.out.println("father do something ");
} finally {
lock.unlock();
}
}
}
static class SubClass extends Father{
@Override
public void method(){
lock.lock();
try {
System.out.println("subClass do something ");
super.method();
} finally {
lock.unlock();
}
}
}
public static void reentrantTest2(){
SubClass subClass = new SubClass();
subClass.method();
}
打印结果:
subClass do something
father do something
ReentrantReadWriteLock
读写锁,它有两个内部类,分别是ReadLock和WriteLock
这两个锁分别有以下特性
多个读锁不互斥(证明)
一个方法内使用readLock.lock锁,当多个线程访问时候,如果线程A持有锁,线程B也可以持有该锁。两个线程无需互相等待。
static class ReadRunnable implements Runnable{
final ReentrantReadWriteLock.ReadLock readLock = new ReentrantReadWriteLock().readLock();
public void run() {
readLock.lock();
System.out.println(Thread.currentThread().getName() + " read start...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " read end...");
readLock.unlock();
}
}
}
public static void readLockNotMutex(){
ReadRunnable readRunnable = new ReadRunnable();
//创建3个多线程,来说明读锁不互斥
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(readRunnable);
thread.start();
}
}
例子说明:创建多个线程,每个线程执行MyRunnable的run方法,run方法中使用读锁设置同步。发现会有多个run方法中打印乱序。
打印结果
Thread-0 read start...
Thread-1 read start...
Thread-2 read start...
Thread-0 read end...
Thread-1 read end...
Thread-2 read end...
说明读锁不互斥
多个写锁互斥(证明)
原理与读锁相反
static class WriteRunnable implements Runnable{
final ReentrantReadWriteLock.WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
public void run() {
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " write start...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " write end...");
writeLock.unlock();
}
}
}
public static void writeLockMutex(){
WriteRunnable writeRunnable = new WriteRunnable();
//创建3个多线程,来说明读锁不互斥
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(writeRunnable);
thread.start();
}
}
创建多个线程,每个线程执行MyRunnable的run方法,run方法中使用写锁设置同步。发现会有多个run方法中打印不乱序。
打印结果:
Thread-0 write start...
Thread-0 write end...
Thread-1 write start...
Thread-1 write end...
Thread-2 write start...
Thread-2 write end...
说明写锁互斥
读锁和写锁互斥
- 线程A执行writeMethod()方法,获取到写锁,
- 此时线程B执行readMethod()方法,因为读写锁(写读锁)互斥,所以要等待线程A释放锁,然后才能继续执行。
实例如下:
static class ReadAndWriteLock{
final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
public void writeMethod(){
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " 当前为write Lock========");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " write lock end ========");
writeLock.unlock();
}
System.out.println(Thread.currentThread().getName() + " 程序结束 end");
}
public void readMethod(){
readLock.lock();
System.out.println(Thread.currentThread().getName() + " 当前为read Lock========");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + " read lock end ========");
readLock.unlock();
}
System.out.println(Thread.currentThread().getName() + " 程序结束 end");
}
}
public static void readWriteMutex(){
ReadAndWriteLock readAndWriteLock = new ReadAndWriteLock();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
readAndWriteLock.writeMethod();
}
});
t.start();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
readAndWriteLock.readMethod();
}
});
t1.start();
}
打印结果
读锁与写锁内容分别顺序打印。说明读写锁互斥。
Thread-0 当前为write Lock========
Thread-0 write lock end ========
Thread-0 程序结束 end
Thread-1 当前为read Lock========
Thread-1 read lock end ========
Thread-1 程序结束 end
读锁不支持Condition操作,而写锁支持
从源码中看到,ReadLock的newCondition方法被禁用了
public Condition newCondition() {
throw new UnsupportedOperationException();
}
而WriteLock可以创建
public Condition newCondition() {
return sync.newCondition();
}
支持降级锁,不支持升级锁
降级锁: 线程进入writeLock,没有释放锁,同时获取到readLock。这时,writeLock会降级为readLock。但是writeLock的锁仍需手动释放。ReentrantReadWriteLock支持降级锁。
public void downGradeLock(){
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 当前进入写锁 write...");
readLock.lock();
System.out.println(Thread.currentThread().getName() + " 当前进入读锁 read...");
} finally {
System.out.println(Thread.currentThread().getName() + " 释放读锁...");
readLock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放写锁...");
writeLock.unlock();
}
System.out.println(Thread.currentThread().getName() + " 程序结束...");
}
打印结果:
程序在持有写锁的同时,可以获得读锁。说明,ReentrantReadWriteLock支持降级锁。
main 当前进入写锁 write...
main 当前进入读锁 read...
main 释放读锁...
main 释放写锁...
main 程序结束...
升级锁: 线程进入readLock,没有释放锁,同时要获取writeLock。
这时,程序进入死锁状态。ReentrantReadWriteLock不支持升级锁。
调用下面的方法,会出现死锁现象。
public void upGradeLock(){
readLock.lock();
System.out.println(Thread.currentThread().getName() + " 当前进入写锁 read...");
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " 当前进入写锁 write...");
writeLock.unlock();
System.out.println("程序结束....");
}
打印结果:
main 当前进入写锁 read...
并且程序一直在等待,尝试获取writeLock。