前言


关于锁其实是解决线程安全性问题的另外一种方案,目前我们学习到解决方案有几种,分别是

  • synchronized
  • volatile
  • Atomic

而synchronized是通吃的但它比较笨重其次是比较繁琐,性能上也不好

因此才出现volatile和Atomic这两种解决方案

那么难道volatile和Atomic这两种就没有局限性了吗?其实是有的

比如说volatile修饰了变量之后,那么只能保证这个变量在线程之间的可见性,但是并不能保证操作的原子性

比如说Atomic这些类仅仅是一个原子性操作当我们要进行判断,要进行等等一系列的操作的时候,它就没有办法保证操作的原子性了

这时我们就需要接着探索看看JDK5之后出现的Lock接口

一、Lock的介绍


首先我们先看看Lock存在那个包下面?

image.png

这就是Lock接口它是从JDK1.5的时候出现的,作者也是并发界的大牛Doug Lea

image.png

常见的方法使用方法即是加锁、释放锁

image.png

image.png

我们知道有时候在多个线程并发的获取锁的时候,那么当有些线程拿不到锁的时候,会等待会不停的去抢占CPU资源

那么在这个过程中在抢占的过程中我们想中断的话怎么办?

我们若是使用使用synchronized是无法做到中断的,那么使用Lock的lockInterruptibly()这个方法在进行加锁的过程中可以中断的,并且抛出一个InterruptedException异常

image.png

还有一个很强大的非阻塞的获取锁tryLock()方法,它可以获取锁如果获取成功返回true,如果没有获取到锁就返回false

image.png

二、Lock如何使用?


接下来我们使用代码体会一下之前我们创建的自增器是如何保证安全的?

public class Sequence {

    private Lock lock = new ReentrantLock();
    
    private static int value;
    
    public int getNext(){

        //加锁
        lock.lock();
        int a =  value++;
        //释放锁
        lock.unlock();
        return a;
    }
}

而当我们去调用使用的时候,就会所有线程使用同一把锁保证线程安全

public static void main(String[] args) {

    Sequence sequence =new Sequence();

    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true){
                System.out.println(Thread.currentThread().getName() +" " +sequence.getNext());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true){
                System.out.println(Thread.currentThread().getName() +" " +sequence.getNext());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }).start();
}
//运行结果如下:
Thread-0 0
Thread-1 1
Thread-0 2
Thread-1 3

这样就没有线程安全性问题了,这就是关于锁的使用

三、使用Lock有什么好处呢?


根据前面的示例我们可以对比发现Lock与synchronized的不同

  • Lock需要显式的获取和释放锁
  • synchronized而不需要显式的获取和释放锁

通过它们之间的比较,我们发现Lock繁琐,synchronized简单

但是可以在任意地方去获取锁和释放锁使得更加的灵活,反而synchronized如果想要实现这样的代码就会非常的复杂

Lock提供了一些强大的方法便捷获取锁的相关信息

  • 非阻塞的获取锁
  • 能被中断的获取锁
  • 超时获取锁

参考资料


龙果学院:并发编程原理与实战(叶子猿老师)


28640
116 声望25 粉丝

心有多大,舞台就有多大