-
Lock简介
锁是控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源。在Lock接口出现之前,Java程序主要是靠synchronized关键字实现锁功能的,而JDK5之后,并发包中增加了lock接口,它提供了与synchronized一样的锁功能。虽然它失去了向synchronized关键字隐式加锁解锁的便捷性,但是却拥有了锁获取和释放的可操作性,可中断的获取锁以及超时获取锁等多种synchronized关键字
2. AQS(AbstractQueuedSynchronizer---同步器)
同步器是用来构建锁和其他同步组件的基础框架,它的实现主要依赖一个int成员变量来表示同步状态以及通过一个FIFO队列构成等待队列。它的子类必须车松懈AQS的几个protected修饰的用来改变同步状态的方法,其他方法主要是实现了排队和阻塞机制。状态的更新使用getState,setState以及compareAndSetState这三个方法。
子类被推荐定义为自定义同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅是定义了若干同步状态的获取和释放方法来供自定义同步组件的使用,同步器既支持独占式获取同步状态,也可以支持共享式获取同步状态,这样就可以方便的实现不同类型的同步组件。
同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中去聚合同步器,利用同步器实现锁的语义。可以这样理解二者的关系:锁是面向使用者,它定义了使用者与锁交互的接口,隐藏了实现细节;同步器是面向锁的实现者,它简化了锁的实现方式,屏蔽了同步状态的管理,线程的排队,等待和唤醒等底层操作。锁和同步器很好的隔离了使用者和实现者所需关注的领域。
AQS的模版方法设计模式
AQS的设计是使用模版设计模式,它将一些方法开放给子类进行重写,而同步器给同步组件所提供的模版方法又会重新调用被子类所重写的方法。
AQS中的模版方法acquire()会调用tryAcquire方法,而此时当继承AQS的NonfairSync调用已经被NonfairSync重写的tryAcquire方法。
使用AQS的方式:
- 同步组件(这里不仅仅指锁,还包括CountDownLatch等)的shixi8na依赖于同步器AQS,在同步组件实现中,使用AQS的方式被推荐定义继承AQS的静态内部类。
- AQS采用模版方法进行设计,AQS的protected修饰的方法需要由继承AQS的子类进行重写实现,当调用AQS的子类的方法时就会调用被重写的方法。
- AQS负责同步状态的管理,线程的排队,等待和唤醒这些底层操作,而Lock等同步组件主要专注于实现同步语义;
- 在重写AQS的方式时,使用AQS提供的getState(),setState(),compareAndSetState()方法进行修改同步状态
AQS可重写的方法:
1.protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}----试图以独占模式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态
2. protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}----独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态
3. protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}----共享式获取同步状态,成功返回大于等于0的值,失败返回小于0的值
4. protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}----共享式释放同步状态
5. public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}-----当前同步器是否在独占模式下被线程占用,一般方法表示是否被当前线程所独占
在实现同步组件时AQS提供的模版方法:
| | |
| | |
| | |
| public final void acquireSharedInterruptibly(long arg) | |
| public final boolean tryAcquireSharedNanos(long arg, long nanosTimeout) | |
| public final boolean release(long arg) { | |
| public final boolean releaseShared(long arg) { | |
| | |
AQS提供的模板方法可以分为3类:
- 独占式获取与释放同步状态;
- 共享式获取与释放同步状态
- 查询同步队列中等待线程情况
在同步组件的实现上主要利用了AQS,而AQS的“屏蔽了”同步状态的修改,线程排队等底层实现,通过AQS的模版方法可以很方便的给同步组件的实现着进行调用。而针对用户来说,只需要抵用同步组件提供的方法来实现并发编程即可。同时在新建一个同步组件时需要把握的两个关键点:
- 实现同步组件时推荐定义继承AQS的静态内部类,并重写需要的protected修饰的方法;
- 同步组件语义的实现依赖于AQS的模版方法,而AQS模版方法又依赖于被AQS的子类所重写的方法
总的来说,同步组件通过重写AQS的实现方法实现自己想要表达的同步语义,而AQS只需要同步组件表达的true和false即可
二、深入理解AQS
- AQS简介
在同步组件的实现中,AQS是核心部分,同步组件的实现者通过使用AQS提供的模板方法实现同步组件语义,AQS则实现了对同步状态的管理,以及对阻塞线程进行排队,等待通知等等一些底层的实现处理。AQS的核心也包括了这些方面:同步队列,独占式锁的获取和释放,共享锁的获取和释放以及可中断锁,超时等待锁获取这些特性的实现,而这些实际是哪个则是AQS提供出来的模版方法。
独占式锁:
- Void acquire(int arg):独占式获取同步状态,如果获取失败则插入同步队列进行等待。
- Void acqireInterruptibly(int arg):与acquire方法相同,但在同步队列中等待时可以响应中断。
- boolean tryAcquire(int arg):获取锁成功返回true,否则返回false
- boolean tryAcquireNanos(int arg,long nanosTimeout):在2的基础上增加了超时等待功能,在超时时间内没有获得同步状态返回false
共享式锁:
void acquireShared(int arg):共享式获取同步状态,与独占锁的区别在于同一时刻有多个线程获取同步状态。
void acquireSharedInterruptibly(int arg):增加了响应中断的功能
Boolean releaseShared(int arg):共享锁释放同步状态