特性
开始时是乐观锁,如果加锁操作频繁就会转换成悲观锁
开始时是轻量级锁,如果锁被占用的时间很长,就会装换成重量级锁.
轻量级锁的实现是使用自旋锁策略
不是一个公平锁 ,
是一个可重入锁,
不是一个读写锁
加锁过程
jvm 将 synchronized 分为四个状态 无锁 , 偏向锁, 自旋锁 , 重量级锁
偏向锁
偏向锁不是立即加锁, 而是在锁的对象头中进行标记 来判断当前的锁是属于哪一个线程,
如果没有其他的线程来参与锁竞争,就不会真正的进行加锁操作,从而降低程序开销,如果有其它的线程来参与锁竞争,就会取消偏向锁状态,进入轻量级锁状态
轻量级锁
随着其他的线程参与锁竞争,synchronized 取消偏向锁的状态,进入轻量级锁的状态----> 自适应的自旋锁 通过CAS实现的
通过CAS检查并实现更新一块内存
如果更新成功, 就认为加锁成功
如果更新失败就认为加锁失败,进入自旋锁,而当自旋的时间或次数到达一定时,就会停止自旋操作这就是所谓的自适应
重量级锁
随着线程竞争进一步激烈,自旋锁不能快速获取到锁的状态,就会膨胀成重量级锁
此处的重量级锁会使用到内核的mutex(互斥锁)
当执行加锁操作时,会进入到内核态,在内核态中判断锁是否被占用,如果没有被占用,就加锁成功,切换回用户态; 如果锁被占用,就会加锁失败,此时线程进入锁的等待队列,挂起,等待被操作系统唤醒, 直到锁被释放,操作系统才唤醒该线程,尝试重新获取锁
其他的优化操作
锁消除
编译器和JVM判断锁是否可以被消除
例如 StringBuffer 创建这个实例,进行append 字符拼接操作,会进行加锁和解锁, 而在单线程中,这并不需要进行加锁解锁操作,此编译器和JVM就会进行锁消除优化操作,防止频繁的加锁解锁,造成资源的浪费
锁粗化
如果一段逻辑当中出现多次的加锁解锁操作,编译器和JVM就会进行锁粗化,锁的粒度分为粗和细,实际开发中使用的细粒度锁,期望释放锁让其它的线程来使用这个锁,但可能实际上并没有其他的线程来使用这个锁,此时JVM就会自动实现锁粗化,避免锁的频繁申请和释放