synchronized的功能的扩展:重入锁

痛定思痛。 2022-02-14 12:53 267阅读 0赞

重入锁

重入锁可以说是synchronized,Object.wait(),Object.notify()的一种替代品。

  1. JDK5的早期版本,重入锁的新能要比synchronized好很多,在JDK6后对synchronized进行可很多优化,使得他和重入锁的性能差距并不大。

重入锁使用java.util.concurrent.locks.ReentrantLock类实现,下面我么来看下重入锁的简单使用案例:

  1. import java.util.*;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class ReenterLock implements Runnable {
  4. public static ReentrantLock lock=new ReentrantLock();
  5. public static int i=0;
  6. @Override
  7. public void run() {
  8. // TODO Auto-generated method stub
  9. for(int j=0;j<10000000;j++) {
  10. lock.lock();
  11. try {
  12. i++;
  13. }finally {
  14. lock.unlock();
  15. }
  16. }
  17. }
  18. public static void main(String[] args) throws InterruptedException {
  19. // TODO Auto-generated method stub
  20. ReenterLock tl=new ReenterLock();
  21. Thread t1=new Thread(tl);
  22. Thread t2=new Thread(tl);
  23. t1.start();
  24. t2.start();
  25. t1.join();
  26. t2.join();
  27. System.out.println(i);
  28. }
  29. }

运行程序可以得到结果为20000000.

重入锁具有很高的灵活性,需要开发人员手动用lock()与unlock()函数来指定何时加锁何时解锁,但是需要注意的是,在离开临界区的时候要记得释放锁,否则其他线程就没有机会在访问临界区了。(之所以叫重入锁是因为这种锁可以反复进入,但是记得一个线程同时获得多少锁,也必须释放相同的次数)

重入锁除了使用上的灵活性,还有一些高级的功能。

中断响应

对于synchronized来说,如果一个线程在等待锁,那么结果只有两种情况,要么获得锁继续执行,要么保持等待。但是使用重入锁。则提供了另外一种可能,就是线程可以被中断。也就是说,在等待锁的过程中,线程可以取消对锁的请求。有些时候这么做是很有必要的,比如和好朋友约好去打球,等了半小时没到,街道电话得知朋友临时有事,不能来了,那么就打道回府了。这种情况对于处理死锁有一定的帮助。使用lockInterruptibly()函数表示重入锁可以响应中断。

锁申请限时等待

除了等待外部通知外,要避免死锁还有一种办法,就是限时等待。我么可以通过tryLock方法进行一次限时的等待。

  1. try{
  2. if(lock.tryLock(5,TimeUnit.SECONDS)){
  3. Thread.sleep(6000);
  4. }else{
  5. System.out.println("get lock failed");
  6. }
  7. }catch(InterruptdeException e){
  8. e.printStackTrace();
  9. }finally{
  10. if(lock.isHeldByCurrentThread()) lock.unlock();}
  11. }

上面代码中,设置的5秒的限时等待,由于睡眠了6秒,或导致请求锁失败。
tryLock也可以不带参数进行运行,这种情况下,线程会尝试获得锁,如果锁未被其他线程占用,申请锁成功,返回true,否则申请失败,线程也不会进行等待,直接返回false。

公平锁

大多数情况下锁是不公平的,也就是说不一定先请求就先获得锁,使用synchronized关键字实现锁,锁就是不公平的,重入锁可以实现公平锁,避免现象,也就是说,只要你排队,就能获得锁,但是由于需要维护一个有序队列,导致公平锁的实现成本比较高,性能相对也非常低下。重入锁有如下一个构造函数:
public ReentrantLock(boolean fair)
当参数为true时锁是公平的的。

重入锁的好搭档:Condition条件

Condition与Object.wait(),Object.notify()方法的作用大致相同,只不过是用来与重入锁相关联的。通过Lock接口(出入锁就实现乐这一接口)的Condition newCondition()方法可以生成与当前重入锁对象绑定的Condition实例,利用Condition对象,我们可以让线程在特定的时刻进行等待,或者在某一个特定的时刻得到通知,继续执行。
Condition接口提供的方法如下。

  1. void await() throws InterrupttedExceptionI;
  2. void awaitUninterruptibly();
  3. long awaitNanos(long nanosTimeout) throws InterrupttedExceptionI;
  4. boolean await(long time,TimeUnit unit) throws InterrupttedExceptionI;
  5. boolean awaitUntil(Date deadline) throws InterrupttedExceptionI;
  6. void signal();
  7. void siganlAll();

以上个方法的含义如下:

  • await()方法会使当前线程等待,同时会释放锁,当其他线程中使用signal()或signalAll()方法的时候,线程会重新获得锁继续执行。或者线程被中断是也能跳出等待。
  • awaitUninterruptibly()方法与await()方法基本相同,但是他不会在等待过程中相应或者相应中断。
  • signal()用于唤醒一个在等待中的线程,同理signalAll()用于唤醒所有在等待中的线程。

发表评论

表情:
评论列表 (有 0 条评论,267人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Synchronize

    Synchronize锁重入 拥有锁重入的功能 就是在使用synchronize时,当一个线程得到了一个对象的锁之后 再次,请求该对象时,可以再次得到该对象的锁