架构师养成记--14.重入锁ReentrantLock 和 读写锁 ReentrantReadWriteLock
ReentrantLock
有嗅探锁定和多路分支等功能,其实就是synchronized,wait,notify的升级。
this锁定当前对象不方便,于是就有了用new Object()来作为锁的解决方案,后面jdk干脆就提供了一个Lock类。
伪代码:
Lock lock = new ReentrantLock();//新建一个lock
Condition condition = lock.newCondition();//获取条件
method1(){
try{
lock.lock();
代码块;
lock.unlock();
后续代码块;
}
}
method2(){
try{
lock.lock();
代码块;
lock.signal();
}
}
单condition操作:
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.Lock; 3 import java.util.concurrent.locks.ReentrantLock; 4 5 public class UseCondition { 6 7 private Lock lock = new ReentrantLock(); 8 private Condition condition = lock.newCondition(); 9 10 public void method1(){ 11 try { 12 lock.lock(); 13 System.out.println("当前线程:" + Thread.currentThread().getName() + "进入.."); 14 Thread.sleep(3000); 15 System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁.."); 16 condition.await(); // Object wait 17 System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行..."); 18 } catch (Exception e) { 19 e.printStackTrace(); 20 } finally { 21 lock.unlock(); 22 } 23 } 24 25 public void method2(){ 26 try { 27 lock.lock(); 28 System.out.println("当前线程:" + Thread.currentThread().getName() + "进入.."); 29 Thread.sleep(3000); 30 System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒.."); 31 condition.signal(); //Object notify 32 } catch (Exception e) { 33 e.printStackTrace(); 34 } finally { 35 lock.unlock(); 36 } 37 } 38 39 public static void main(String[] args) { 40 41 final UseCondition uc = new UseCondition(); 42 Thread t1 = new Thread(new Runnable() { 43 @Override 44 public void run() { 45 uc.method1(); 46 } 47 }, "t1"); 48 Thread t2 = new Thread(new Runnable() { 49 @Override 50 public void run() { 51 uc.method2(); 52 } 53 }, "t2"); 54 t1.start(); 55 56 t2.start(); 57 } 58 59 60 61 }
执行结果:
解释:线程1调用method1方法,线程2调用同一对象的method2方法,线程1先启动,线程1获取锁,进入method1的代码块,线程2也启动了,单是线程2被锁定,直到线程1发出lock.unlock()了,线程1才将锁释放,这时线程2获取锁,执行method2的代码块,线程2发出lock.signal(); 线程1才继续执行后续代码块。
多condition操作:
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 public class UseManyCondition { 5 6 private ReentrantLock lock = new ReentrantLock(); 7 private Condition c1 = lock.newCondition(); 8 private Condition c2 = lock.newCondition(); 9 10 public void m1(){ 11 try { 12 lock.lock(); 13 System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m1等待.."); 14 c1.await(); 15 System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m1继续.."); 16 } catch (Exception e) { 17 e.printStackTrace(); 18 } finally { 19 lock.unlock(); 20 } 21 } 22 23 public void m2(){ 24 try { 25 lock.lock(); 26 System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m2等待.."); 27 c1.await(); 28 System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m2继续.."); 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } finally { 32 lock.unlock(); 33 } 34 } 35 36 public void m3(){ 37 try { 38 lock.lock(); 39 System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m3等待.."); 40 c2.await(); 41 System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m3继续.."); 42 } catch (Exception e) { 43 e.printStackTrace(); 44 } finally { 45 lock.unlock(); 46 } 47 } 48 49 public void m4(){ 50 try { 51 lock.lock(); 52 System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒.."); 53 c1.signalAll(); 54 } catch (Exception e) { 55 e.printStackTrace(); 56 } finally { 57 lock.unlock(); 58 } 59 } 60 61 public void m5(){ 62 try { 63 lock.lock(); 64 System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒.."); 65 c2.signal(); 66 } catch (Exception e) { 67 e.printStackTrace(); 68 } finally { 69 lock.unlock(); 70 } 71 } 72 73 public static void main(String[] args) { 74 75 76 final UseManyCondition umc = new UseManyCondition(); 77 Thread t1 = new Thread(new Runnable() { 78 @Override 79 public void run() { 80 umc.m1(); 81 } 82 },"t1"); 83 Thread t2 = new Thread(new Runnable() { 84 @Override 85 public void run() { 86 umc.m2(); 87 } 88 },"t2"); 89 Thread t3 = new Thread(new Runnable() { 90 @Override 91 public void run() { 92 umc.m3(); 93 } 94 },"t3"); 95 Thread t4 = new Thread(new Runnable() { 96 @Override 97 public void run() { 98 umc.m4(); 99 } 100 },"t4"); 101 Thread t5 = new Thread(new Runnable() { 102 @Override 103 public void run() { 104 umc.m5(); 105 } 106 },"t5"); 107 108 t1.start(); // c1 109 t2.start(); // c1 110 t3.start(); // c2 111 112 113 try { 114 Thread.sleep(2000); 115 } catch (InterruptedException e) { 116 e.printStackTrace(); 117 } 118 119 t4.start(); // c1 120 try { 121 Thread.sleep(2000); 122 } catch (InterruptedException e) { 123 e.printStackTrace(); 124 } 125 t5.start(); // c2 126 127 } 128 129 130 131 }
执行结果:
解释:t1、t2线程都是用第一个condition c1,t3线程用第二个condition c2,t4线程发出c1.signalAll() 唤醒t1和t2线程,t5发出c2.signal()唤醒t3线程。
ReentrantReadWriteLock
读写分离锁,读读共享、写写互斥、读写互斥。
1 import java.util.concurrent.locks.ReentrantReadWriteLock; 2 import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; 3 import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; 4 5 public class UseReentrantReadWriteLock { 6 7 private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); 8 private ReadLock readLock = rwLock.readLock(); 9 private WriteLock writeLock = rwLock.writeLock(); 10 11 public void read(){ 12 try { 13 readLock.lock(); 14 System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..."); 15 Thread.sleep(3000); 16 System.out.println("当前线程:" + Thread.currentThread().getName() + "退出..."); 17 } catch (Exception e) { 18 e.printStackTrace(); 19 } finally { 20 readLock.unlock(); 21 } 22 } 23 24 public void write(){ 25 try { 26 writeLock.lock(); 27 System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..."); 28 Thread.sleep(3000); 29 System.out.println("当前线程:" + Thread.currentThread().getName() + "退出..."); 30 } catch (Exception e) { 31 e.printStackTrace(); 32 } finally { 33 writeLock.unlock(); 34 } 35 } 36 37 public static void main(String[] args) { 38 39 final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock(); 40 41 Thread t1 = new Thread(new Runnable() { 42 @Override 43 public void run() { 44 urrw.read(); 45 } 46 }, "t1"); 47 Thread t2 = new Thread(new Runnable() { 48 @Override 49 public void run() { 50 urrw.read(); 51 } 52 }, "t2"); 53 Thread t3 = new Thread(new Runnable() { 54 @Override 55 public void run() { 56 urrw.write(); 57 } 58 }, "t3"); 59 Thread t4 = new Thread(new Runnable() { 60 @Override 61 public void run() { 62 urrw.write(); 63 } 64 }, "t4"); 65 66 t1.start(); 67 t2.start(); 68 69 // t1.start(); // R 70 // t3.start(); // W 71 72 // t3.start(); 73 // t4.start(); 74 } 75 }
解释:t1和t2都是读,是共享的,可同时获得readLock同时进入read方法执行。t1是读和t3是写,是互斥的,只有t1执行完t3才能执行。t3、t4都是写,是互斥的,只有等t3执行完t4才能继续。