- Lock接口
- Lock接口概述
- API方法
- 锁获取与中断
- Synchronized和Lock的区别
Lock接口
大佬地址:
AQS(AbstractQueuedSynchronizer)源码深度解析(2)—Lock接口以及自定义锁的实现
Lock接口概述
Lock接口同样自于JDK1.5,它被描述成JUC中的锁的超级接口,所有的JUC中的锁都会实现Lock接口。
由于它是作为接口,到这里或许大家都明白了它的设计意图,接口就是一种规范。Lcok接口定义了一些抽象方法,用于获取锁、释放锁等,而所有的锁都实现Lock接口,那么它们虽然可能有不同的内部实现,但是开放给外部调用的方法却是一样的,这就是一种规范,无论你怎么实现,你给外界调用的始终是“同一个方法”!因此JUC中的锁也被常常统称为lock锁。
API方法
方法名称 | 描述 |
---|---|
lock | 获取锁,如果锁无法获取,那么当前的线程被挂起,直到锁被获取到,不可被中断。 |
lockInterruptibly | 获取锁,如果获取到了锁,那么立即返回,如果获取不到,那么当前线程被挂起,直到当前线程被唤醒或者其他的线程中断了当前的线程。 |
tryLock | 如果调用的时候能够获取锁,那么就获取锁并且返回true,如果当前的锁无法获取到,那么这个方法会立刻返回false |
tryLcok(long time,TimeUnit unit) | 在指定时间内尝试获取锁,当前线程在以下3种情况下会返回: ①如果当前线程在超时时间内获取了锁,那么返回true。 ②如果当前的锁无法获取,那么当前的线程被挂起,直到当前线程获取到了锁或者当前线程被其他线程中断或者指定的等待时间到了。 ③超时时间结束还没有获取到锁则返回false。 |
unlock | 释放当前线程占用的锁 |
newCondition | 返回一个与当前的锁关联的条件变量。在使用这个条件变量之前,当前线程必须占用锁。调用Condition的await方法,会在等待之前原子地释放锁,并在等待被唤醒后原子的获取锁 |
锁获取与中断
Thread类中有一个interrupt方法,可中断因为主动调用Object.wait()、Thread.join()和Thread.sleep()等方法造成的线程等待,以及使用lockInterruptibly方法和tryLock(time,timeUnit)尝试获取锁但是未获取锁而造成的阻塞,并且他们都将抛出InterruptedException异常,并且设置该线程的中断状态位为false。
但是对于因为调用lock()方法,或者因为无法获取Synchronized锁而被阻塞的线程,interrupt方法无法中断,仅会设置中断标志位为true,另外对于正常线程,同样仅会设置中断标志位为true,通知该线程应该被中断了。有一个特例是:被LockSupport.park()阻塞的线程也可以被中断,但是不会抛出异常,并且不会恢复标志位。
下面举例详细说明Lock接口的这四种方法的使用:假如线程A和线程B使用同一个锁LOCK,此时线程A首先获取到锁LOCK.lock(),并且始终持有不释放。
如果此时B要去获取锁,有四种方式:
LOCK.lock(): 此方式会使得B始终处于等待中,即使调用B.interrupt()也不能中断,除非线程A调用LOCK.unlock()释放锁。
LOCK.lockInterruptibly(): 此方式会使得B等待,但当调用B.interrupt()会被中断等待,并抛出InterruptedException异常,否则会与lock()一样始终处于等待中,直到线程A释放锁。
LOCK.tryLock(): 调用该方法时B不会等待,一次获取不到锁就直接返回false。
LOCK.tryLock(10, TimeUnit.SECONDS): 该处会在10秒时间内处于等待中,但当调用B.interrupt()会中断等待,并抛出InterruptedException。 10秒时间内如果线程A释放锁,线程B会获取到锁并返回true,否则10秒过后会直接返回false,去执行下面的逻辑。
Synchronized和Lock的区别
- 首先synchronized是java内置关键字,是jvm层面实现的;Lock是个java接口,可以代表JUC中的锁,通过java代码实现;synchronized关键字可以直接修饰方法,也可以修饰代码块,而lock只能修饰代码块。
- synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;即lock需要显示的获得、释放锁,synchronized隐式获得、释放锁。
- lock在等待锁过程中可以用
lockInterruptibly()
方法配合interrupt方法来中断等待,也可以使用tryLock()
设置等待超时时间,而synchronized只能等待锁的释放,不能响应中断。
- synchronized是非公平锁,而Lock锁可以实现为公平锁或者非公平锁。
- Lock锁将监视器monitor方法,单独的封装到了一个Condition对象当中,更加的面向对象了,而且一个Lock锁可以拥有多个condition对象(条件队列),singal和signalAll可以选择唤醒不同的队列中的线程;而同一个synchronized块只能有一个监视器对象,一个条件队列。
- Lock可以提高多个线程进行读操作的效率,非常灵活。(可以通过readwritelock实现读写分离)。
- synchronized使用Thread.holdLock(监视器对象)检测当前线程是否持有锁,Lock可以通过lock.trylock或者isHeldExclusively方法判断是否获取到锁。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。