重入锁
重入锁可以说是synchronized,Object.wait(),Object.notify()的一种替代品。
在JDK5的早期版本,重入锁的新能要比synchronized好很多,在JDK6后对synchronized进行可很多优化,使得他和重入锁的性能差距并不大。
重入锁使用java.util.concurrent.locks.ReentrantLock类实现,下面我么来看下重入锁的简单使用案例:
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
public class ReenterLock implements Runnable {
public static ReentrantLock lock=new ReentrantLock();
public static int i=0;
@Override
public void run() {
// TODO Auto-generated method stub
for(int j=0;j<10000000;j++) {
lock.lock();
try {
i++;
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ReenterLock tl=new ReenterLock();
Thread t1=new Thread(tl);
Thread t2=new Thread(tl);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
运行程序可以得到结果为20000000.
重入锁具有很高的灵活性,需要开发人员手动用lock()与unlock()函数来指定何时加锁何时解锁,但是需要注意的是,在离开临界区的时候要记得释放锁,否则其他线程就没有机会在访问临界区了。(之所以叫重入锁是因为这种锁可以反复进入,但是记得一个线程同时获得多少锁,也必须释放相同的次数)
重入锁除了使用上的灵活性,还有一些高级的功能。
中断响应
对于synchronized来说,如果一个线程在等待锁,那么结果只有两种情况,要么获得锁继续执行,要么保持等待。但是使用重入锁。则提供了另外一种可能,就是线程可以被中断。也就是说,在等待锁的过程中,线程可以取消对锁的请求。有些时候这么做是很有必要的,比如和好朋友约好去打球,等了半小时没到,街道电话得知朋友临时有事,不能来了,那么就打道回府了。这种情况对于处理死锁有一定的帮助。使用lockInterruptibly()函数表示重入锁可以响应中断。
锁申请限时等待
除了等待外部通知外,要避免死锁还有一种办法,就是限时等待。我么可以通过tryLock方法进行一次限时的等待。
try{
if(lock.tryLock(5,TimeUnit.SECONDS)){
Thread.sleep(6000);
}else{
System.out.println("get lock failed");
}
}catch(InterruptdeException e){
e.printStackTrace();
}finally{
if(lock.isHeldByCurrentThread()) lock.unlock();}
}
上面代码中,设置的5秒的限时等待,由于睡眠了6秒,或导致请求锁失败。
tryLock也可以不带参数进行运行,这种情况下,线程会尝试获得锁,如果锁未被其他线程占用,申请锁成功,返回true,否则申请失败,线程也不会进行等待,直接返回false。
公平锁
大多数情况下锁是不公平的,也就是说不一定先请求就先获得锁,使用synchronized关键字实现锁,锁就是不公平的,重入锁可以实现公平锁,避免现象,也就是说,只要你排队,就能获得锁,但是由于需要维护一个有序队列,导致公平锁的实现成本比较高,性能相对也非常低下。重入锁有如下一个构造函数:
public ReentrantLock(boolean fair)
当参数为true时锁是公平的的。
重入锁的好搭档:Condition条件
Condition与Object.wait(),Object.notify()方法的作用大致相同,只不过是用来与重入锁相关联的。通过Lock接口(出入锁就实现乐这一接口)的Condition newCondition()方法可以生成与当前重入锁对象绑定的Condition实例,利用Condition对象,我们可以让线程在特定的时刻进行等待,或者在某一个特定的时刻得到通知,继续执行。
Condition接口提供的方法如下。
void await() throws InterrupttedExceptionI;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterrupttedExceptionI;
boolean await(long time,TimeUnit unit) throws InterrupttedExceptionI;
boolean awaitUntil(Date deadline) throws InterrupttedExceptionI;
void signal();
void siganlAll();
以上个方法的含义如下:
- await()方法会使当前线程等待,同时会释放锁,当其他线程中使用signal()或signalAll()方法的时候,线程会重新获得锁继续执行。或者线程被中断是也能跳出等待。
- awaitUninterruptibly()方法与await()方法基本相同,但是他不会在等待过程中相应或者相应中断。
- signal()用于唤醒一个在等待中的线程,同理signalAll()用于唤醒所有在等待中的线程。