关于java:什么是可中断锁有什么用怎么实现

49次阅读

共计 3953 个字符,预计需要花费 10 分钟才能阅读完成。

在 Java 中有两种锁,一种是内置锁 synchronized,一种是显示锁 Lock,其中 Lock 锁是可中断锁,而 synchronized 则为不可中断锁。

所谓的 中断锁指的是锁在执行时可被中断,也就是在执行时能够接管 interrupt 的告诉,从而中断锁执行

PS:默认状况下 Lock 也是不可中断锁,然而能够通过非凡的“伎俩”,能够让其变为可中断锁,接下来咱们一起来看。

为什么须要可中断锁?

不可中断锁的问题是,当呈现“异样”时,只能始终阻塞期待,别无其他方法,比方上面这个程序。上面的这个程序中有两个线程,其中线程 1 先获取到锁资源执行相应代码,而线程 2 在 0.5s 之后开始尝试获取锁资源,但线程 1 执行时遗记开释锁了,这就造成线程 2 始终阻塞期待的状况,实现代码如下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class InterruptiblyExample {public static void main(String[] args) {Lock lock = new ReentrantLock();

        // 创立线程 1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {lock.lock();
                System.out.println("线程 1:获取到锁.");
                // 线程 1 未开释锁
            }
        });
        t1.start();

        // 创立线程 2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 先休眠 0.5s,让线程 1 先执行
                try {Thread.sleep(500);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                // 获取锁
                System.out.println("线程 2: 期待获取锁.");
                lock.lock();
                try {System.out.println("线程 2:获取锁胜利.");
                } finally {lock.unlock();
                }
            }
        });
        t2.start();}
}

以上代码执行的后果如下:

从上述后果能够看出,此时线程 2 在期待获取锁的操作,然而经验了 N 久之后 …

再次查看后果,仍然是相熟的画面:

线程 2 还在阻塞期待获取线程 1 开释锁资源,此时的线程 2 除了等之外,并无其余办法。

并且,但咱们纯熟的拿出了 JConsole,试图失去一个死锁的具体信息时,却失去了这样的后果:

并没有检测到任何死锁信息,从上图咱们能够看出,当只有一个锁资源的时候,零碎并不会把这种状况断定为死锁,当然也没有阻塞期待的具体信息喽,此时只剩下线程 2 孤独地期待着它的“锁儿”。

应用中断锁

然而,中断锁的呈现,就能够突破这一僵局,它能够在期待肯定工夫之后,被动的中断线程 2,以解决线程阻塞期待的问题。

中断锁的外围实现代码是 lock.lockInterruptibly() 办法,它和 lock.lock() 办法作用相似,只不过应用 lockInterruptibly 办法能够优先接管中断的申请,中断锁的具体实现如下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class InterruptiblyExample {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock();

        // 创立线程 1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 加锁操作
                    lock.lock();
                    System.out.println("线程 1: 获取到锁.");
                } catch (InterruptedException e) {e.printStackTrace();
                }
                // 线程 1 未开释锁
            }
        });
        t1.start();

        // 创立线程 2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 先休眠 0.5s,让线程 1 先执行
                try {Thread.sleep(500);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                // 获取锁
                try {System.out.println("线程 2: 尝试获取锁.");
                    lock.lockInterruptibly(); // 可中断锁
                    System.out.println("线程 2: 获取锁胜利.");
                } catch (InterruptedException e) {System.out.println("线程 2: 执行已被中断.");
                }
            }
        });
        t2.start();

        // 期待 2s 后, 终止线程 2
        Thread.sleep(2000);
        if (t2.isAlive()) { // 线程 2 还在执行
            System.out.println("执行线程的中断.");
            t2.interrupt();} else {System.out.println("线程 2: 执行实现.");
        }
    }
}

以上代码执行后果如下:

从上述后果能够看出,当咱们应用了 lockInterruptibly 办法就能够在一段时间之后,判断它是否还在阻塞期待,如果后果为真,就能够间接将他中断,如上图成果所示。

但当咱们尝试将 lockInterruptibly 办法换成 lock 办法之后(其余代码都不变),执行的后果就齐全不一样了,实现代码如下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class InterruptiblyExample {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock();

        // 创立线程 1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 加锁操作
                    lock.lockInterruptibly();
                    System.out.println("线程 1: 获取到锁.");
                } catch (InterruptedException e) {e.printStackTrace();
                }
                // 线程 1 未开释锁
            }
        });
        t1.start();

        // 创立线程 2
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 先休眠 0.5s,让线程 1 先执行
                try {Thread.sleep(500);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                // 获取锁
                try {System.out.println("线程 2: 尝试获取锁.");
                    lock.lock();
                    System.out.println("线程 2: 获取锁胜利.");
                } catch (Exception e) {System.out.println("线程 2: 执行已被中断.");
                }
            }
        });
        t2.start();

        // 期待 2s 后, 终止线程 2
        Thread.sleep(2000);
        if (t2.isAlive()) { // 线程 2 还在执行
            System.out.println("执行线程的中断.");
            t2.interrupt();} else {System.out.println("线程 2: 执行实现.");
        }
    }
}

以上程序执行后果如下:

从上图能够看出,当应用 lock 办法时,即便调用了 interrupt 办法仍然不能将线程 2 进行中断。

总结

本文介绍了中断锁的实现,通过显示锁 Lock 的 lockInterruptibly 办法来实现,它和 lock 办法作用相似,但 lockInterruptibly 能够优先接管到中断的告诉,而 lock 办法只能“死等”锁资源的开释,同时这两个办法的区别也是常见的面试题,心愿本文对你有用。

并发原创文章举荐

  1. 线程的 4 种创立办法和应用详解!
  2. Java 中用户线程和守护线程区别这么大?
  3. 深刻了解线程池 ThreadPool
  4. 线程池的 7 种创立形式,强烈推荐你用它 …
  5. 池化技术达到有多牛?看了线程和线程池的比照吓我一跳!
  6. 并发中的线程同步与锁
  7. synchronized 加锁 this 和 class 的区别!
  8. volatile 和 synchronized 的区别
  9. 轻量级锁肯定比重量级锁快吗?
  10. 这样终止线程,居然会导致服务宕机?
  11. SimpleDateFormat 线程不平安的 5 种解决方案!
  12. ThreadLocal 不好用?那是你没用对!
  13. ThreadLocal 内存溢出代码演示和起因剖析!
  14. Semaphore 自白:限流器用我就对了!
  15. CountDownLatch:别浪,等人齐再团!
  16. CyclicBarrier:人齐了,司机就能够发车了!
  17. synchronized 优化伎俩之锁收缩机制!
  18. synchronized 中的 4 个优化,你晓得几个?
  19. ReentrantLock 中的 4 个坑!
  20. 图解:为什么非偏心锁的性能更高?
  21. 死锁的 4 种排查工具!
  22. 死锁终结者:程序锁和轮询锁!
  23. 轮询锁在应用时遇到的问题与解决方案!

关注公号「Java 中文社群」查看更多有意思、涨常识的 Java 并发文章。

正文完
 0