JAVA语言除了提供内置锁synchronized,还在JDK6之后提供了高级的显式锁Lock作为功能上的补充。在大多数情况下,内置锁都能很好的工作,我们也尽量使用内置锁。但必须承认的是,内置锁存在一些局限性。比如:无法中断一个正在等待获取锁的线程;无法在请求一个锁时无限等待下去;无法实现非阻塞的加锁规则。当程序中需要这些高级的功能时,考虑使用显式锁。我们来介绍2种显示锁,Lock和ReadWriteLock。
下面是Lock的定义:
public interface Lock {
//获得锁,在得到锁之前无限等待
void lock();
//获得锁,在得到锁之前允许线程被中断
void lockInterruptibly() throws InterruptedException;
//返回true,代表获得锁成功;返回false,代表获得锁失败
boolean tryLock();
//返回true,代表在指定时间内获得锁成功;返回false,代表在指定时间内获得锁失败
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放锁
void unlock();
//返回一个用于线程间通信的实例,关于Condition接口的介绍,参见博客<条件队列(二)>
Condition newCondition();
}
ReentrantLock是一个实现了Lock接口的类,是一个可重入锁(博客《显式锁(二)》有介绍),下面是一些例子。
//lock()的使用
class Test{
private Lock lock=new ReentrantLock();
public void fun(){
lock.lock();
try{
}catch(Exception e){
}finally{
lock.unlock();
}
}
}
//tryLock()的使用
class Test{
private Lock lock=new ReentrantLock();
public void fun(){
if(lock.tryLock()){
try{
}catch(Exception e){
}finally{
lock.unlock();
}
}else{
}
}
}
//lockInterruptibly()的使用
class Test{
private Lock lock=new ReentrantLock();
public void fun() {
try{
lock.lockInterruptibly();
try{
}catch(Exception e){
}finally{
lock.unlock();
}
}catch(InterruptedException e){
//抛出异常或者恢复中断
}
}
}
下面是ReadWriteLock的定义:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
ReadWriteLock的好处是在保证写操作安全性的前提下,能够使读操作并发进行(即:允许并发的读读,但必须互斥的读写,写读,写写)。ReentrantReadWriteLock是实现ReadWriteLock接口的类,使用方法如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Demo{
public static void main(String [] args) throws InterruptedException {
final Test test = new Test();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
}
}
class Test {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private Lock rLock=rwl.readLock();
private Lock wLock=rwl.writeLock();
public void get(Thread thread) {
rLock.lock();
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 100) {
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
} finally {
rLock.unlock();
}
}
}
synchronized和Lock的区别:
1. synchronized自动释放锁,Lock必须在finally块中手动释放锁
2. Lock能够提供一些高级功能,比如:非阻塞的加锁,等待时响应中断,无限等待等
3. synchronized是内置锁,由JVM支持;Lock是JAVA语言级别的锁机制
4. synchronized是非公平锁;Lock默认是非公平锁,但可以设置为公平锁。
注意:在上面的所有例子中,你会发现,在获得锁时,语句都是在try块之前,这是必须的。因为如果放到try块里面,在获取锁发生unchecked异常的时,将触发一次释放锁的操作。但实际上,此时并没有占有锁,为此会导致另一个异常IllegalMonitorStateException。
笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?
提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496