Java并发编程之ReenTrantLock
2018-01-11 14:55
471 查看
如果锁具备可重入性,则称作为可重入锁。像
synchronized和
ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个
synchronized方法时,比如说
method1,而在
method1中会调用另外一个
synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法
method2。
可中断锁:顾名思义,就是可以相应中断的锁。在Java中,
synchronized就不是可中断锁,而
Lock是可中断锁。如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。在前面演示
lockInterruptibly()的用法时已经体现了
Lock的可中断性。
公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。在Java中,
synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于
ReentrantLock和
ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。我们可以在创建
ReentrantLock对象时,通过以下方式来设置锁的公平性:
ReentrantLock lock = new ReentrantLock(true);如果参数为
true表示为公平锁,为
fasle为非公平锁。默认情况下,如果使用无参构造器,则是非公平锁。
读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。
ReadWriteLock就是读写锁,它是一个接口,
ReentrantReadWriteLock实现了这个接口。可以通过
readLock()获取读锁,通过
writeLock()获取写锁。
/**
* 默认构造方法,非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* true公平锁,false非公平锁
* @param fair
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
以下是一个简单的使用实例
public class ThreadTest {
public static void main(String[] args) {
Counter counter = new Counter();
for (int i = 0; i < 5; i++) {
new Thread(() -> counter.run()).start();
}
}
}
class Counter {
private final Lock lock = new ReentrantLock();
public void run() {
try {
lock.lock();
for(int i = 0 ; i < 3 ; i++){
System.out.println(Thread.currentThread().getName() + "run......");
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
从名字上理解,
ReenTrantLock的字面意思就是再进入的锁,其实
synchronized关键字所使用的锁也是可重入的,两者关于这个的区别不大。两者都是同一个线程没进入一次,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。
synchronized是依赖于
JVM实现的,而
ReenTrantLock是
JDK实现的,有什么区别,说白了就类似于操作系统来控制实现和用户自己敲代码实现的区别。前者的实现是比较难见到的,后者有直接的源码可供阅读。
synchronized在
JVM层面实现,不但可以通过一些监控工具监控锁定,而且在代码执行出现异常,
JVM自动释放锁定;
Lock是通过代码实现,为了保证锁定一定会被释放,一般会将
unLock()放到
flyinal{}中。
在资源竞争不激烈的情况下,
synchronized的性能要优于
ReentrantLock,但在资源竞争很激烈的情况下,
synchronized的性能会下降几十倍,但是
ReentrantLock的性能能维持常态。
在
Synchronized优化以前,
synchronized的性能是比
ReenTrantLock差很多的,但是自从
Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用
synchronized,其实
synchronized的优化我感觉就借鉴了
ReenTrantLock中的
CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。
便利性:很明显
Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而
ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在
finally中声明释放锁。
锁的细粒度和灵活度:很明显
ReenTrantLock优于
Synchronized。
ReenTrantLock可以指定是公平锁还是非公平锁。而
synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。
ReenTrantLock提供了一个
Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像
synchronized要么随机唤醒一个线程,要么唤醒全部线程。
tryLock(long timeout, TimeUnit unit),如果获取了锁定立即返回
true,如果别的线程正持有锁,将等待参数给定的时间,在等待的过程中,如果获取了锁定,返回
true,如果等待超时,返回
false,所以
lock()方法相当
trylock传递个无限大的时间参数;
ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过
lock.lockInterruptibly()来实现这个机制。
lockInteruptibly,如果获取了锁定立即返回,反之,当前线程处理休眠,直至获取锁,或者当前线程线程被其他线程中断。
tryLock(),如果获取了锁立即返回
true,如果别的线程下持有,立即返回
false;
使用
synchronized时,如果A不释放,B将一直等待下去,无法中断。
使用
ReentrantLock时,如果A不释放,B可以在等待足够长时间后,停止等待,继续执行其他事务。
一般情况下,只有在我们需要实现特定的功能时,会使用
ReentrantLock。
相关文章推荐
- 【Java并发编程实战】—–“J.U.C”:ReentrantLock之二lock方法分析
- Java并发编程之ReentrantLock
- Java并发编程 - ReentrantLock(重入锁)以及公平性
- JAVA并发编程学习笔记之ReentrantLock (r)
- Java并发编程之显示锁ReentrantLock和ReadWriteLock读写锁
- JAVA并发编程学习笔记之ReentrantLock
- 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock
- JAVA并发编程学习笔记之ReentrantLock
- Java 并发编程 --- 读写锁:ReentrantReadWriteLock
- 【Java并发编程实战】—–“J.U.C”:ReentrantReadWriteLock
- 并发编程学习总结(四) :java 显式锁ReentrantLock使用详解之lock()\unlock() 加锁与释放锁
- 【Java并发编程实战】-----“J.U.C”:ReentrantLock之三unlock方法分析
- 并发编程学习总结(五) :java 显式锁ReentrantLock使用详解之条件对象(2)
- 【Java并发编程实战】-----“J.U.C”:ReentrantLock之一简介
- 深入Java多线程和并发编程之ReentrantLock
- 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock
- 【Java并发编程实战】-----“J.U.C”:ReentrantLock之二lock方法分析
- Java 并发编程之ReentrantLock和synchronized锁
- java并发编程-线程同步-显示锁ReentrantLock
- Java并发编程 ReentrantLock 源码分析