lock
synchronized(非公平锁)(隐式获取锁,取锁解锁过程固话)
分类
- 普通同步方法
- 锁定当前实例对象
- 静态同步方法
- 锁定当前类
- 同步代码块
- 锁synchronize括号里面配置的对象
原理 进入和退出monitor
- 代码块 两者配对出现
- monitorenter 被持有后出于锁定状态,执行enter时,尝试获得monitor所有权
- 每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
- 1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
- 2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
- 3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
- monitorexit
- 执行monitorexit的线程必须是objectref所对应的monitor的所有者。
- 指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
- 通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
- https://www.cnblogs.com/paddix/p/5367116.html
- monitorenter 被持有后出于锁定状态,执行enter时,尝试获得monitor所有权
- 方法
- 方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
- 对象,监视器,同步队列,执行线程关系
- 任意线程访问被synchronized保护的对象访问步骤
- 获取object的监视器
- 获取失败进入同步队列,线程状态变为block
- 获得锁的线程释放锁,唤醒block线程尝试获取监视器
- 任意线程访问被synchronized保护的对象访问步骤
lock接口
ReentrantLock
- 分类
- 公平锁
- 公平锁就是在获取锁之前会先判断等待队列是否为空或者自己是否位于队列头部,该条件通过才能继续获取锁
- 保证fifo进行大亮线程切换
- 公平锁就是在获取锁之前会先判断等待队列是否为空或者自己是否位于队列头部,该条件通过才能继续获取锁
- 非公平锁
- 保证吞吐量,可能会存在饥饿,极少线程切换
- 公平锁
- 实现
- 通过组合自定义同步器实现锁的获取和释放,同一个获取锁的线程可以再次进入,
ReentrantReadWriteLock
- 新增
- 公平性选择
- 重进入
- 锁降级,写锁能够降级成读锁
- 持有写锁,获取读锁,释放写锁
- 实现
- 按位切割使用变量,高16读状态,低16写状态
- 读锁获取需要等其他线程写锁释放,写锁被获取,其他读写线程的后续访问将被阻塞
新特性
- 尝试非阻塞的获取锁
- 能被中断的获取锁
- 超时获取锁
主要方法
内存语义
内存模型
locksuppport
阻塞或者唤醒线程,使用locksupport完成相应工作
condition()
与lock配合实现等待通知模式
- 子主题 1
- 等待
- 调用addConditionWaiter将当前线程加入等待队列;
- 调用fullRelease释放当前线程节点的同步状态,唤醒后继节点;
- 线程进入等待状态;
- 线程被唤醒后,从while循环中退出,调用acquireQueued尝试获取同步状态;
- 同步状态获取成功后,线程从await方法返回。
- 作者:miaoLoveCode
- 链接:https://www.jianshu.com/p/be2dc7c878dc
- 唤醒
- step1:前置检查,判断当前线程是否是获取了锁的线程,如果不是抛出异常IllegalMonitorStateException,否则,执行step2;
- step2:取得等待队列的头结点,头结点不为空执行doSignal,否则,signal结束。
- 整个doSignal完成了这两个操作:调用transferForSignal将节点从等待队列移动到同步队列,并且,将该节点从等待队列删除。
- step1:将节点waitStatus设置为0,设置成功执行step2,否则返回false;
- step2:调用enq方法将该节点加入同步队列;
- step3:使用LockSuppor.unpark()方法唤醒该节点的线程。
- 整个doSignal完成了这两个操作:调用transferForSignal将节点从等待队列移动到同步队列,并且,将该节点从等待队列删除。
XMind: ZEN - Trial Version