重入锁
为什么叫重入锁?是因为同一个线程可以重复进入自己获取的锁.
1.重入锁的特性
1.1重入锁比synchronized更灵活,它能够显式指定何时加锁,何时解锁.(实例方法lock(),unlock())
1.2中断响应
使用ReentrantLock的实例方法lockInterruptibly()方法(建议使用可以响应中断的锁),可以在线程要求中断的时候进行响应,避免死锁情况下一直等待.
public class ReentrantLockInterruptDemo implements Runnable { public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int i; public ReentrantLockInterruptDemo(int i){ this.i = i; } @Override public void run() { // TODO Auto-generated method stub try { if(i==1){ //对锁的请求,统一使用lockInterruptibly方法,这是一个可以对中断进行响应的锁申请动作.在等待锁的时候可以响应中断 lock1.lockInterruptibly(); try { Thread.sleep(500); } catch (Exception e) { // TODO: handle exception } lock2.lockInterruptibly(); }else{ //对锁的请求,统一使用lockInterruptibly方法,这是一个可以对中断进行响应的锁申请动作.在等待锁的时候可以响应中断 lock2.lockInterruptibly(); try { Thread.sleep(500); } catch (Exception e) { // TODO: handle exception } lock1.lockInterruptibly(); } } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); }finally{ if(lock1.isHeldByCurrentThread()){ lock1.unlock(); }else if(lock2.isHeldByCurrentThread()){ lock2.unlock(); } System.out.println(Thread.currentThread().getId()+":线程退出"); } } /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub Thread t1 = new Thread(new ReentrantLockInterruptDemo(1)); Thread t2 = new Thread(new ReentrantLockInterruptDemo(2)); t1.start(); t2.start(); Thread.sleep(2000); t2.interrupt(); } }
1.3锁申请等待限时
申请锁的时候(使用tryLock(timeout, unit)方法),可以指定等待时间,如果在指定时间内没有获取到锁,返回false,否则返回true;而无参的tryLock()方法,当前线程会去尝试获得锁,如果锁被其他线程占用,则立即返回false,不会引起线程等待.
public class ReentrantLockTryLockDemo implements Runnable { public static ReentrantLock lock = new ReentrantLock(); @Override public void run() { try { System.out.println("我进来了,我要获取锁,我是_"+Thread.currentThread().getName()); if(lock.tryLock(5, TimeUnit.SECONDS)){ System.out.println("谁进来了?我是_"+Thread.currentThread().getName()); Thread.sleep(6000); }else{ System.out.println("没获取到锁,我走了_"+Thread.currentThread().getName()); } } catch (Exception e) { e.printStackTrace(); }finally{ if(lock.isHeldByCurrentThread()){ lock.unlock(); } } } /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { ReentrantLockTryLockDemo rt = new ReentrantLockTryLockDemo(); Thread t1 = new Thread(rt); Thread t2 = new Thread(rt); t1.start(); t2.start(); } }
1.4公平锁设置
重入锁可以对锁的公平性进行设置.在ReentrantLock的构造函数中.公平锁需要维护一个队列(按照线程的进入顺序,不论优先级高低).
但是由于实现成本较高,性能比较低,因此默认情况下是非公平的.
public class ReentrantLockFairDemo implements Runnable { //设置公平锁 public static ReentrantLock fairLock = new ReentrantLock(true); @Override public void run() { while(true){ try { fairLock.lock(); System.out.println("我是_"+Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); }finally{ fairLock.unlock(); } } } /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { ReentrantLockFairDemo rt = new ReentrantLockFairDemo(); Thread t1 = new Thread(rt,"Thread_t1"); Thread t2 = new Thread(rt,"Thread_t2"); t1.start(); t2.start(); }
1.5重入锁的好伙伴:Condition条件对象
在synchronized中,我们可以使用Object对象提供的wait()和notify()方法实现线程的挂起和唤醒(线程Thread中的suspend和resume已经废弃).
换句话说,Object对象中的wait()和notify()方法是和synchronized合用起来完成线程的挂起和唤醒操作的.
在重入锁中,我们使用Condition条件对象与重入锁合用,来实现线程的挂起(await)与唤醒(singal).
在使用时,我们需要使用ReentrantLock中的实例方法newCondition()得到Condition对象,将重入锁与对应的Condition对象进行绑定,实现线程的等待与唤醒.
具体方法介绍如下:
await()方法:与Object类中的wait()方法类似,在Condition接口中,提供了方法await(),调用后使当前线程等待,同时释放当前锁.在等待过程中可以响应中断.
awaitUninterruptibly()方法:功能与await()一样,不同的是,在等待过程中不响应中断.
singal()方法:用于唤醒一个在等待队列中的线程.
singalAll()方法:用于唤醒所有在等待队列中的线程.
public class ReentrantLockCondition implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static Condition condition = lock.newCondition(); @Override public void run() { try { //加锁 lock.lock(); System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"获取锁执行等待...."); //等待,并释放锁 condition.await(); System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"开始执行任务...."); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"执行完成...."); lock.unlock(); System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"线程释放锁...."); } } /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub Thread t1 = new Thread(new ReentrantLockCondition()); Thread t2 = new Thread(new ReentrantLockCondition()); t1.start(); t2.start(); Thread.sleep(2000); System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"线程获取锁...."); //重新获取锁 lock.lock(); //唤醒 System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"线程通知T1...."); condition.signalAll(); //释放锁 lock.unlock(); System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"释放锁...."); } }