Java中的锁机制主要分为Synchronized
和Lock
Synchronized
Synchronized在JVM里的实现是基于Monitor来实现的, Monitor是依赖与底层的操作系统Mutex Lock(互斥锁)来实现的线程同步.
synchronized用的锁是存在Java对象头里的.
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步. 代码块同步是使用monitorenter和monitorexit指令实现的, monitorenter指令是在编译后插入到同步代码块的开始位置, 而monitorexit是插入到方法结束处和异常处.
任何对象都有一个monitor与之关联, 当且一个monitor被持有后,它将处于锁定状态.
根据虚拟机规范的要求, 在执行monitorenter指令时, 首先要去尝试获取对象的锁, 如果这个对象没被锁定, 或者当前线程已经拥有了那个对象的锁, 把锁的计数器加1. 相应地, 在执行monitorexit指令时会将锁计数器减1, 当计数器被减到0时, 锁就释放了. 如果获取对象锁失败了, 那当前线程就要阻塞等待,直到对象锁被另一个线程释放为止.
- synchronized同步快对同一条线程来说是可重入的, 不会出现自己把自己锁死的问题
- 同步块在已进入的线程执行完之前, 会阻塞后面其他线程的进入.
- 可重入, 不可中断, 非公平锁
Lock
Lock底层是基于AQS(AbstractQueuedSynchronizer)的, AQS是用来构建锁或者其他同步组件的基础框架, 它使用来一个int成员变量表示同步状态, 通过内置的FIFO队列来完成资源获取线程的排队工作.
使用时必须手动进行上锁, 解锁.
Lock接口有多种实现, 比如ReentrantLock和ReadWriteLock.
Lock可以通过tryLock来获取是否可以获取锁的状态.
- 可重入, 可以选择是否公平锁
- 可以响应在等待锁时的中断
AQS
这个类中几个主要的变量:
- status (保存当前的状态)
- head/tail (保存队列) 主要的几个方法:
- acquire(int)
- release(int)
- acquireShared(int)
- releaseShared(int) 分别表示独占式的加锁与释放锁的方法 共享锁的加速与释放锁的方法