synchronized
Reentrantlock
ReentrantReadWriteLock
为什么需要锁?
为什么 JAVA 有了 synchronize 还需要 Reentrantlock 和 ReentrantReadWriteLock?
synchronize 和 lock 分别怎么实现同步快(原子性,一致性,禁重排序)?
synchronize 和 lock 分别怎么实现锁的优化,可重入锁,偏向锁
lock 如何实现公平锁(synchronize 是非公平锁)
目的:
锁的目的是防止资源的竞争,主要从原子性(一致性),可见性,防止处理重排序 三个方面来处理,volatile 满足了后面两个特性,JAVA 从两方面来实现锁
synchronized 与 ReentrantLock ,使用上看区别
1, synchronize 在获取锁阻塞的时候是不能打断的
2, synchronize 无超时机制,阻塞了的话只能一直阻塞造成死锁
3,synchronize 只能 notify,wait,如果需要两个或以上条件就不能用了,如: JAVA 阻塞队列的实现,需要用是否为空和是否已满两个条件来阻塞线程
看 lock 相关的 API 就知道, 主要就是解决这几个问题
方法名称 | 描述 |
lock | 获取锁,如果锁无法获取,那么当前的线程就变为不可被调度,直到锁被获取到 |
lockInterruptibly | 获取锁,除非当前线程被中断。如果获取到了锁,那么立即返回,如果获取不到,那么当前线程变得不可被调度,一直休眠直到下面两件事情发生:1、当前线程获取到了锁
2、其他的线程中断了当前的线程 |
tryLock | 如果调用的时候能够获取锁,那么就获取锁并且返回 true,如果当前的锁无法获取到,那么这个方法会立刻返回 false |
tryLcok(long time,TimeUnit unit) | 在指定时间内尝试获取锁如果可以获取锁,那么获取锁并且返回 true,如果当前的锁无法获取,那么当前的线程变得不可被调度,直到下面三件事之一发生:1、当前线程获取到了锁
2、当前线程被其他线程中断 3、指定的等待时间到了 |
unlock | 释放当前线程占用的锁 |
newCondition | 返回一个与当前的锁关联的条件变量。在使用这个条件变量之前,当前线程必须占用锁。调用 Condition 的 await 方法,会在等待之前原子地释放锁,并在等待被唤醒后原子的获取锁 |
读写锁用于读多写少的情况,即当一条线程获取写锁后,后面的读锁都被阻塞,等待获取写锁的线程完成释放。
场景,如本地缓存失效,当需要去 DB 拿数据进行写入的操作,需要阻塞其它读的操作.
当然,读写锁也是可以基于 notifyAll 和 wait 实现
需要注意的是
synchronized 锁
实现依赖
原子性,可见性和重排序都是依靠指令。方法同步和代码块同步依靠 Monitor 指令,代码块同步是使用 monitorenter 和 monitorexit 指令实现
锁信息保存在 JAVA 对象头里,准确说是 Mark Word
synchronize 的阻塞,依靠几个队列,属于不公平锁(线程先 CAS 竞争锁,再进队列)
ContentionList(LIFO)-->EntryList(LIFO)-->OnDeck-->Owner-->Wait Set ( )
锁的转换方面
无锁 --> 偏向锁 --> 轻量锁 --> 重量锁 ()
偏向锁和可重入锁的实现
可重入锁,即当本线程进入同一锁时可以进行多次上锁,当然也需要多次释放
偏向锁,即当获取线程再次进入同步块时不需要再次竞争 (CAS), 当某个 Core CAS 成功时必然会引起总线风暴,这就是所谓的本地延迟,本质上偏向锁就是为了消除 CAS,降低 Cache 一致性流量
原理:
1, 锁保存当前锁的线程,判断同一个线程时允许
用一个计数器去记录当前重入的次数,当进入时计数器 + 1, 当释放锁时计算器 - 1, 当为 0 时表示可竞争
synchronized
实现方式, 会在 Mark Word 存储获取锁线程的 ID,然后栈帧中也存储线程 ID,以后该线程再次进入同步块(同步方法)时不需要花费 CAS 了。
lock
实现方式, 用 JAVA 代码实现处理,跟踪下 lock()(NonfairSyncCAS 获取锁失败)->acquire(AQS 尝试获取锁)-->tryAcquire(nonfairTryAcquire)-->nonfairTryAcquire(Sync 如下处理):
- final boolean nonfairTryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- if (c == 0) { //再次判断是否是未锁状态,state为0为未有线程获取锁
- if (compareAndSetState(0, acquires)) { //CAS再次竞争获取锁,此处是公平锁与非公平锁的区别
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- //这里是是实现偏向锁的关键,比较如果是当前锁就不进入CLH队列后面的竞争了
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0) // overflow
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
synchronize 本身是非公平锁,无公平性实现.
lock 非公平锁代码(详细如上)
- if (compareAndSetState(0, acquires)) { //如锁被释放,是非公平锁的话,用CAS再次竞争获取锁
公平锁此段代码如下:
- protected final boolean tryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- if (c == 0) {
- if (!hasQueuedPredecessors() &&
- compareAndSetState(0, acquires)) {//如锁被释放,必须当队列为空时才去CAS竞争锁
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
欢迎关注我的公众号, 一起来构建我们的知识体系
来源: http://www.cnblogs.com/springsource/p/6555734.html