一、概述
在Java1.5版本以前,我们开发多线程程序只能通过关键字synchronized进行共享资源的同步、临界值的控制,虽然随着版本的不断升级,JDK对synchronized关键字的性能优化工作一直都没有停止过,但是synchronized在使用的过程中还是存在着比较多的缺陷和不足,比如:等待synchronized 锁的线程无法被中断,无法设置超时时间,无法获取当前有多少线程被阻塞。在JDK 1.5 版本以后引入了 Lock 显示锁,Lock 锁不但具备 synchronized 关键字提供的所有功能外,还解决了 synchronized 锁存在的问题。
二、Lock 接口简介
Lock 接口是对锁操作方法的一个基本定义,它提供了 synchronized 关键字所具备的全部功能方法,另外我们可以借助于Lock创建不同的Condition对象进行多线程间的通信操作,与关键字synchronized进行方法同步代码块同步的方式不同,Lock提供了编程式的锁获取(lock())以及释放操作(unlock())等其他操作。Lock 中的接口如下所示:
public interface Lock {
/**
* 尝试获取锁,
* 如果此刻该锁未被其他线程持有,则会立即返回,并且设置锁的hold计数为1;
* 如果当前线程已经持有该锁则会再次尝试申请,hold计数将会增加一个,并且立即返回;
* 如果该锁当前被另外一个线程持有,那么当前线程会进入阻塞,直到获取该锁,
* 由于调用lock方法而进入阻塞状态的线程同样不会被中断,这一点与进入synchronized同步方法或者代码块被阻塞类似
*/
void lock();
/**
* 该方法的作用与前者类似,但是使用该方法试图获取锁而进入阻塞操作的线程则是可被中断的,也就说线程可以获得中断信号
*
* 如果锁未被占用,并且当前线程获取到了锁,则会立即返回.
* 如果锁已被占用,当前线程则会进入休眠,直到以下中的某一种情况发生:
* 1、当前线程申请到了锁。
* 2、其他线程中断了当前线程,并且支持锁中断
*/
void lockInterruptibly() throws InterruptedException;
/**
* 调用该方法获取锁,无论成功与否都会立即返回,线程不会进入阻塞状态,
* 若成功获取锁则返回true,若获取锁失败则返回false。
* 使用该方法时请务必注意进行结果的判断,否则会出现获取锁失败却仍旧操作共享资源而导致数据不一致等问题的出现。
*/
boolean tryLock();
/**
* 该方法与tryLock()方法类似,只不过多了单位时间设置,
* 如果在单位时间内未获取到锁,则返回结果为false,
* 如果在单位时间内获取到了锁,则返回结果为true,同样hold计数也会被设置为1。
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 当某个线程对锁的使用结束之后,应该确保对锁资源的释放,
* 以便其他线程能够继续争抢,unlock()方法的作用正在于此。
*/
void unlock();
/**
* 创建一个与该lock相关联的Condition对象。
*/
Condition newCondition();
}
三、实现类
Lock的实现类如下所示:
Lock 接口的实现类中主要的就是 重入锁 ReentrantLock 和 读写锁 Readlock、WriteLock。
四、使用锁的示例
Lock锁又称为 显示锁,在使用时需要手动的获取锁和释放锁,Lock锁的使用范式 如下所示:
//定义一个锁
Lock lock = ...
//获取锁
lock.lock();
try{
}finally {
// 在finally 中释放锁
lock.unlock();
}
五、Lock 与 synchronized 的比较
- Lock是一个接口,是JDK层面的实现;而synchronized是Java中的关键字,是Java的内置特性,是JVM层面的实现;
- synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
- Lock 可以让等待锁的线程响应中断,而使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
通过Lock可以知道有没有成功获取锁,而synchronized却无法办到; - Lock 可用设置获取锁的超时时间,在指定时间内未获取到锁则返回,而 synchronized 无法做到, synchronized 获取不到锁,线程则会一直死等。
- Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的。而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。