一、Lock接口提供的Synchronized关键字不具备的主要特性
特性 | 描述 |
尝试非阻塞地获取锁 | 当前线程尝试获取锁,如果这一时刻没有被其他线程获取到,则成功并持有锁 |
能被中断的获取锁 | 与Synchronized不同,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会释放 |
超时获取锁 | 在指定的截止时间之前获取锁,如果截止时间到了仍旧无法获取锁,则返回 |
二、同步队列器
同步队列器(AbstractQueueSynchronizer),是用来构建锁或者其他同步组件的基础架构,他用了一个int型成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,主要使用是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。
重写同步器指定的方法时,需要使用同步器提供的如下三个方法。
getstate():获取当前同步状态
setState(int newState):设置当前同步状态
compareAndSetState(int expect,int update):使用CAS设置当前状态,该方法能够保证状态设置的原子性。
1独占式同步状态获取和释放:在获取同步状态时,同步器维护了一个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列(或停止自旋)的条件是前驱节点为头节点并且成功获取了同步状态。在释放同步状态时,同步器调用tryRelease(int arg)方法释放同步状态,然后唤醒头结点的后继节点。
2写操作要求对资源的独占式访问,而读操作可以是共享式访问。调用同步器的acquireShared(int arg)方法可以共享式地获取同步状态。在该方法中,同步器调用tryAcquireShared(int arg)方法尝试获取同步状态,tryAcquireShared(int arg)方法返回值为int 类型,当返回值大于等于0时,表示能够获取同步状态。因此,在共享式获取的自旋过程中,成功获取到同步状态并退出自旋的条件就是tryAcquire(int arg)返回值大于等于0.
三、重入锁ReentrantLock
Synchronized隐式的支持重进入。ReentrantLock支持重进入,即该锁能够支持一个线程对资源的重复加锁。除此之外,该锁还支持获取锁时的公平和非公平性选择。
简单来说,ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。想尽办法避免线程进入内核的阻塞状态是我们去分析和理解锁设计的关键钥匙。
ReentrantLock提供了一个构造函数,能够控制锁是否公平。
事实上公平锁的效率往往没有非公平的效率高,但是并不是任何场景都是以TPS作为唯一的指标,公平锁能减少饥饿的发生的概率,等待越久越是能够得到优先满足。
1可重入实现:两者都是同一个线程每进入一次,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。
2读写锁:之前提到锁(如Mutex和ReentrantLock)基本都是排它锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他线程均被阻塞。读写锁维护了一对锁,一个读锁和写锁,通过分离读锁和写锁,使得并发性相比一般的排它锁有了很大提升。
ReentrantReadWriteLock的特性
特性 | 说明 |
公平性选择 | 支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平 |
重进入 | 该锁支持重进入,以读写线程为例:读线程在获取了读锁之后,能够再次获取读锁。而写线程在获取了写锁之后能够再次获取写锁,同时也可以获取读锁。 |
锁降级 | 遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。 |
锁降级:把持住写锁(当前拥有的),再获取到读锁,随后释放(先前拥有的)写锁的释放过程。
四、condition接口
任意一个对象,都拥有一组监视器方法(定义在Java.lang.Object上),主要包括wait(),wait(long),notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式。但是这两者在使用方式以及功能特性上还是有差别的。
Object监视器方法和Condition接口对比
对比项 | Object Monitor Methods | Condition |
前置条件 | 获取对象锁 | 调用Lock.lock()获取锁 调用Lock.newCondition()获取Condition对象 |
调用方法 | 直接调用:如object.wait() | 直接调用 如:condition.await() |
等待队列个数 | 一个 | 多个 |
当前线程释放锁并进入等待状态 | 支持 | 支持 |
当前线程释放锁并进入等待状态,在等待 状态中不响应中断。 | 不支持 | 支持 |
当前线程释放锁并进入超时等待 | 支持 | 支持 |
当前线程释放锁并进入等待状态到将来的某个时间 | 不支持 | 支持 |
唤醒等待队列中的一个线程 | 支持 | 支持 |
唤醒等待队列中的全部线程 | 支持 | 支持 |