Java并发编程十 重入锁ReentrantLock 读写锁ReentrantReadWriteLock
Java的concurrent并发包提供了一套与Synchronized关键字相同功能Lock,还有与wait()/notify()相同功能的Condition。
Lock的功能比传统的Synchronized更加灵活。它具有嗅探锁定和多路分支等功能(一个lock对象可以创建多个Condition)。
1.Lock和synchronized的区别和各自的特点
1、类型不同
Lock是一个接口,是JDK层面的实现;synchronized是Java的关键字,是JVM层面的实现,是Java的内置特性;这也造成了在锁管理上的不同。
2、释放锁的方式
Lock是在编码中进行锁的操作,需要主动调用接口中的方法释放锁,所以使用Lock时需要配合try模块,在finally中释放锁,不然一旦发生异常很可能造成死锁现象;synchronized由于是JVM层面的实现,获取锁和释放锁的操作是不可见的,当发生异常时,锁的释放由JVM实现。
3、获取锁的过程
Lock接口中提供的方法,让获取锁的过程更加灵活,编程人员可以方便的在获取锁的过程中进行多种操作,比如尝试获取锁、设置获取锁的超时时间、中断等待获取锁的线程等,应该说让锁的操作变得“丰富多彩”起来;synchronized是无法这么灵活的对锁进行操作的。
4、效率
基于读写锁接口的扩展,Lock可以提高多个线程进行读操作的效率,在大并发量的情况下,效率的提高会尤其明显。
2.ReentrantLock
ReentrantLock 重入锁 它与Synchronized关键字的功能相同只是需要自己手动释放锁,而线程间的通信机制wait()/notify()也有相应的代替实现Condition。其原理和Synchronized是一样的。
/**
* Created by lyyz on 18-5-17.
*/
public class ReentrantLock_eg {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
Condition condition2 = reentrantLock.newCondition();
public void method1(){
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"进入mehtod1");
System.out.println(Thread.currentThread().getName()+"condition1 休眠");
condition1.await();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"完成mehtod1");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public void method2(){
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"进入mehtod2");
System.out.println(Thread.currentThread().getName()+"condition2 休眠");
condition2.await();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"完成mehtod2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public void method3(){
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"进入mehtod3");
System.out.println(Thread.currentThread().getName()+"唤醒condition1 休眠状态的线程 ");
condition1.signal();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"完成mehtod3");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public void method4(){
try {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName()+"进入mehtod4");
System.out.println(Thread.currentThread().getName()+"休眠");
System.out.println(Thread.currentThread().getName()+"唤醒condition2 休眠状态的线程 ");
condition2.signal();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"完成mehtod4");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public static void main(String[] args) {
final ReentrantLock_eg reentrantLock_eg = new ReentrantLock_eg();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
reentrantLock_eg.method1();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
reentrantLock_eg.method2();
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
reentrantLock_eg.method3();
}
},"t3");
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
reentrantLock_eg.method4();
}
},"t4");
t1.start();
t2.start();
t3.start();
t4.start();
// run output
// t1进入mehtod1
// t1condition1 休眠
// t2进入mehtod2
// t2condition2 休眠
// t3进入mehtod3
// t3唤醒condition1 休眠状态的线程
// t3完成mehtod3
// t1完成mehtod1
// t4进入mehtod4
// t4休眠
// t4唤醒condition2 休眠状态的线程
// t4完成mehtod4
// t2完成mehtod2
}
}
3.ReentrantReadWriteLock
ReentrantReadWriteLock 读写锁,多用于读多写少的情况。它的特点就是 读读共享 写写互斥,读写互斥
/**
* Created by lyyz on 18-5-17.
*/
public class ReentrantReadWriteLock_eg {
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public void read(){
try {
readLock.lock();
System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
} catch (Exception e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
}
public void write(){
try {
writeLock.lock();
System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
} catch (Exception e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
public static void main(String[] args) {
ReentrantReadWriteLock_eg reentrantReadWriteLock_eg = new ReentrantReadWriteLock_eg();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
reentrantReadWriteLock_eg.read();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
reentrantReadWriteLock_eg.read();
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
reentrantReadWriteLock_eg.write();
}
},"t3");
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
reentrantReadWriteLock_eg.write();
}
},"t4");
//读读共享,两个线程可同时进行读取
// t1.start();
// t2.start();
// 当前线程:t2进入...
// 当前线程:t1进入...
// 当前线程:t2退出...
// 当前线程:t1退出...
//写写互斥 一个线程执行完以后另一个线程才能执行
// t3.start();
// t4.start();
// 当前线程:t3进入...
// 当前线程:t3退出...
// 当前线程:t4进入...
// 当前线程:t4退出...
//读写互斥
// t1.start();
// t3.start();
// 当前线程:t1进入...
// 当前线程:t1退出...
// 当前线程:t3进入...
// 当前线程:t3退出...
}
}