ReentrantLock
在实现线程同步时,大多情况下使用的是Synchronized关键字实现,但是Synchronized的锁太过封闭,获得锁和释放锁都是系统自动的。所以为了更灵活的操作,在JDK1.5版本之后,就诞生了Lock接口以及其实现类。
lock接口也是对资源上锁的一种工具,它不同于Synchronized的隐式上锁与解锁,lock接口中的获取锁和释放锁,是需要手动去操作的。
Synchronized关键字的同步代码出现以下三种情况才会释放锁:
- 1、同步代码块执行完成,自动释放锁。
- 2、同步代码块出现异常或者错误,此时释放锁。
- 3、当前获得锁的线程状态置为Waiting或者Time_Waitiing,此时释放锁。(置为Waitiing也就是对象锁调用了wait()方法,或者 lock对象对应的condition对象中的await()方法)。
Synchronized的缺陷:
- 缺点1:多个线程去获取锁资源,只有一个线程可以获得锁,其他线程只能阻塞,什么事情都做不了。因此,需要一种机制让处于阻塞的线程可以中断对锁的获取,也就是抛弃某些线程。(解决方案:Lock接口中的lockInterruptibly(),或者trylock(long time,TimeUnit unit))。
- 缺点2:当文件被读写时,读操作和写操作同样要获取当前文件的锁资源,Synchronized有且仅有一个线程访问当前文件并进行读或者写。此时效率会低下。因此,需要一种机制来使得当多个线程都只是进行读操作时,线程之间不会发生冲突。(解决方案:ReadWriteLock接口实现类:ReentrantReadWtriteLock)。
- 缺点3:我们无法得知当前线程是否获得了锁。因此我们需要一个方法来查看当前锁是否获取到了。(解决方案 : Lock接口中的tryLock())。
Lock中的方法
lock(); //获取锁
unlock(); //释放锁
trylock(); //尝试获取锁,获取到了返回true,获取不到返回false; 可轮询
trylock(long time ,TimeUnit unit); // 在一定时间内获取锁,获取到了返回true,获取不到返回false;可定时的
lockInterruptibly(); //获取锁,但是可以被中断 可响应中断
//具体意思就是这个方法和lock是一样的,
//都是获取锁的方法,但是它可以被interrupt中断,中断了获取锁的状态,
//就好比直接将当前线程抛弃。
Condition newCondition(); //生成一个Condition,可以对锁对象的进行等待,唤醒等操作,也就是用于线程通信
- ReentrantLock使用格式:
Lock lock = new ReentrantLo
lock.lock();//加锁
try{
//共享代码
}catch{
}finally{
lock.unlock(); //释放锁
}
public class TestDemo {
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+" got the lock");
}finally {
System.out.println(Thread.currentThread().getName()+" release the lock");
lock.unlock();
}
}
}
ReentrantLock释放锁是显式的,有可能会忘记清除锁,使用起来更加"危险"。
ReentrantLock是Lock接口的唯一实现子类。
ReentrantLock是一个可重入锁。什么叫做可重入锁呢,就是当前线程中的方法对锁资源进行获取之后,方法内部中还有一个线程对锁进行获取,此时就不需要再获取锁了,直接进入。这就是可重入锁。Synchronized也是可重入锁,在讲解monitor中就可以看出。
ReentrantLock的实现方式
ReentrantLock主要利用CAS+CLH队列来实现。它支持公平锁和非公平锁,两者的实现类似。
- 非公平锁:随机的获取,谁运气好,谁先获得cpu使用权,哪个线程就先获取执行。
- 公平锁:第一次获取锁的线程,在下一次执行也会优先获取锁。
public ReentrantLock();//无参构造函数 -> 非公平锁
public ReentrantLock(boolean fair);//true表示公平锁,false表示非公平锁
public class TestDemo {
private static final Lock lock = new ReentrantLock(true); //公平锁
public static void test(){
for(int i=0; i<3; i++){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+" got the lock");
TimeUnit.