20-Lock接口认识与使用

本讲来了解一下关于Lock,Lock顾名思义就是锁,我们之前也已经了解过了关于锁,它其实是解决线程安全性问题的另外一种方案,解决线程安全性问题,我们已经学习了哪些呢?第一个就是synchronized;第二个,我们了解了volatile。这两个可以追溯的历史是比较远的,synchronized在JDK多线程出现的时候,就出现。第三个是 ,JDK5新特性中新加入的goc包中的Atomic,Atomic类有很多。我们发现synchronized是通吃的,就是,所有的线程安全性问题,synchronized都可以解决,那么,为什么还会出现volatile和Atomic这两种解决方案呢?其实就是因为,使用synchronized首先是比较笨重,其次是比较繁琐,性能上也不好,因此才出现volatile和Atomic这两种解决方案,于是,按照这个逻辑下去,我们发现volatile和Atomic这两种解决方案都有局限性,比如说,volatile修饰了变量之后,那么,只能保证这个变量在线程之间的可见性,但是,并不能保证操作的原子性,Atomic这些类,我们发现,仅仅是一个原子性操作,就是说,它的功能依然是有限的,比如说我们要进行判断,要进行等等一系列的操作的时候,它就没有办法保证操作的原子性了。我们如果不想使用synchronized的话,那么就还需要进行探索,那么,这就是JDK5之后出现的Lock接口,也就是说,这个Lock接口也是用来解决线程安全性问题的,而且Lock接口是在JDK5的guc包中发行的,那么,我们就来看一下这个接口,那么,这里我们就要了解几个问题,第一个问题是,Lock相比较synchronized有哪些好处;第二个问题是,Lock如何使用。后面我们还要了解Lock的原理,对于Lock的原理的内容是比较多的,所以,我们会分开来进行讲解,

Lock接口是在这个包下面的,

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

Lock接口中的方法并不多

这个就是上锁,加锁。

这个就是释放锁。

一个是获取锁,一个是释放锁。

那么,其他的方法是干什么的呢?我们知道,在多个线程并发的获取锁的时候,那么,当有些线程拿不到锁的时候,会等待,会不停的去抢占CPU资源,那么,在这个过程中,在抢占的过程中,使用synchronized是无法做到中断的,那么,使用Lock的lockInterruptibly()这个方法在进行加锁的过程中,如果在进行抢占锁的时候,发出来一个中断,那么,lockInterruptibly()是可以中断的,并且抛出一个InterruptedException异常。

tryLock()就更强大了,这个叫做非阻塞的获取锁,意思就是,我调用一下tryLock(),返回一个boolean值,也就是说,我就在这一时刻发出一个tryLock,那么,它就会获取锁,如果获取成功,返回true,否则返回false。如果没有获取到锁,就返回false。那么,这个过程是在瞬时间完成的,那么,tryLock()就会立刻给我们返回值,这就是所谓的tryLock()。当然了,Lock,远非我们想的这么简单,比如说,我们之前也提到过关于公平的问题,我们说,锁,是没有保证公平性可言的,但是,我们却有这样的需求,就是说,你执行一次我执行一次,那么,这种情况下又该如何保证,就是保证公平性,怎么做呢?其实,使用synchronized解决起来就会非常的麻烦,当然是可以解决的。这里,Lock就会给我们提供,比如说我们这里看到有一个实现类叫做ReentrantLock

这里面有一个构造方法

这个参数就是fair,是否是公平的,如果是公平的,那么,它所实现的锁就是一个公平锁,那么,在使用的过程中就会非常方便了。

那么,除了这些以外,比如说还有提高读写性能的

读写锁。

这个是JDK8中所出现的,这个到我们讲JDK8新增的问题的时候再去学习。

这个就是传说中的AQS,可能对于开发者来讲,对这个接口并不是太熟悉。但是,我们会发现,如果读源码的话,这个类的功能是非常强大的。包括以后我们要了解的信号量等等都用到了这个类。这个类出现的也是比较早的JDK1.5就出现了。

下面我们就看一下Lock如何去使用。

这段代码是什么意思呢?lock.lock();就相当于获取锁,就类似于进入到了同步代码块中。lock.unlock();就是释放锁,就相当于退出了这个synchronized。发现,上图红框中这样写有没有问题呢?每次进来都会创建一个锁对象,Lock lock = new ReentrantLock();那就锁不住了,就相当于每一次进来拿不同的对象,

发现这样写还是会出现线程安全性问题。

应该这样写,把它拿出来,所有的线程共用一把锁才能够锁的住,否则的话是锁不住的。

这样就没有线程安全性问题了。

这就是关于锁的使用。

使用我们已经了解了,那么,我们说使用Lock有什么好处呢?我们为什么要使用Lock呢?第一,我们发现,Lock需要显式的获取和释放锁,而synchronized不需要显式的获取和释放锁。这是我们发现的一个规律。

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

那么,既然使用了麻烦的Lock还导致了我们的代码更加的繁琐,那么,何必还要用Lock呢?Lock繁琐带来的其实有更大的好处,为什么呢?比如说,让我们的代码更加灵活,繁琐能让代码更灵活,如何让代码更灵活呢?

我们想,比如说,在获取一个锁的时候,并且释放另外一把锁,我们这里可以创建多把锁,

锁的获取和释放都是我们自己去管理的,那么,也就让我们的代码更加的灵活。你可以在任意的地方去获取锁和释放锁,而synchronized如果想要实现这样的代码就会非常的复杂。另外,那么,为了能够保证锁一定能够释放,

我们可以把它放到finally代码块里面去进行释放。这是关于第一个,就是Lock的灵活性。

第二个,使用Lock可以方便的实现公平性。其实Lock我们到后面就会知道,它其实也会用到synchronized,因此Lock相当于就是对synchronized的一个包装,我们经常说不要重复造轮子,那么,想要实现一个公平性,那么,我们就使用已经实现Lock接口的Lock类就可以了,而不再需要我们再去实现一大堆的代码来保证公平性,因此Lock更便捷,它类似于一个工具,这是第二个。

第三个,Lock锁里面可以提供非常多的一些便捷的操作,比如说我们之前已经提到的,它可以非阻塞的获取锁,比如说tryLoc;它还可以在获取锁的时候,能被中断,除了这两个以外,其实还有一个,就是它可以超时获取锁,

那么,超时获取锁,当设置一定的超时时间,当这个时间超过了之后,如果没有获取,就返回,如果在设定的时间内获取到了,也返回,这就是关于超时获取锁。到后面我们会详细的来研究关于锁的一个实现,我们也会自己来实现一把锁,且保证这些特性。另外我们发现,其实,synchronized也并非一无是处,它还是有它的简单的一些特性。如果我们不需要非常复杂的锁的操作的话,那么,我们依然是可以使用synchronized的。其实我们可以就简单的理解就是Lock对synchronized进行了一个包装,变得更为强大。关于Lock接口的认识与使用就说到这里。

猜你喜欢

转载自blog.csdn.net/G_66_hero/article/details/86261704