java synchronized与lock的区别
首先synchronized与lock(已ReentrantReadWriteLock为例,下面都简称lock)都是用来解决java线程同步问题,但是它们存在以下不同点
概念与使用
synchronized
synchronized是java保留的关键字,是java对象的内置锁。线程进入同步代码时会获取该锁,退出时会释放,进入退出完全由java虚拟机自动管理。synchronized可以直接用来修饰对象,方法,代码块,类(修饰对象,方法,代码块与类会有不同的效果,这里就不细将了)。
lock
lock是人为实现的一个java对象,lock的加锁,解锁都需要手动去触发。使用lock时需要先创建一个lock对象,再通过使用lock()与unlock()来加锁解锁。
原理
synchronized
synchronized是基于java对象的内置锁的,什么是java内置锁呢?
我们知道java对象在内存中的布局分为三部分:对象头,实例数据与对齐填充。对象头又包括几部分:
- Mark Word(存储对象的hashCode,锁信息或分代年龄或GC标志等信息)
- Class MetaData Address (类型指针指向对象的元数据,JVM通过该指针确定对象属于哪个类的实例)
- Array length(只有当前对象为数组的时候才存在,表示当前数组长度)
synchronized
其中Mark Word的主要结构如下(借用一张图):
说明:我们可以看到Mark Word部分大小为32bit(32位系统下),但是内容不固定
- 无锁状态时,32bit包含了hashcode,age(GC分代年龄),偏向锁标志与锁标志。
- 轻量级锁:由于线程被锁住,不会被GC回收,同时也不需要偏向锁标志,而hashcode则由于位置不够被转移到Monitor(稍后解释)中。所以比较简单,只有锁记录指针(指向Monitor对象地址)与锁标志。
- 重量级锁:同轻量级锁
- GC标记:对象将要被回收,所以不需要其他额外信息
- 偏向锁:由于内存不够,hashcode同样移到了Monitor中,相对于无锁状态多出了线程id(偏向锁所偏向的线程),Epoch(偏向时间戳)
synchronized则是利用对象头信息和Monitor对象联合实现。
关于Monitor这里有篇文章讲的不错
Monitor:也被称为管程或监视器锁,基于C++ ObjectMonitor实现。每个对象都存在一个Monitor与之对应,对象与其 monitor 之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。
ObjectMonitor的几个主要属性如下
-
_owner:指向持有ObjectMonitor对象的线程
-
_WaitSet:存放处于wait状态的线程队列
-
_EntryList:存放处于等待锁block状态的线程队列
-
_recursions:锁的重入次数
-
_count:用来记录该线程获取锁的次数
当多个线程同时访问一段同步代码时,首先会进入_EntryList队列中,当某个线程获取到对象的monitor后进入_Owner区域并把monitor中的_owner变量设置为当前线程,同时monitor中的计数器_count加1。即获得对象锁。
若持有monitor的线程调用wait()方法,将释放当前持有的monitor,_owner变量恢复为null,_count自减1,同时该线程进入_WaitSet集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。
lock
以ReentrantReadWriteLock的WriteLock为例,我们来看看源码
我们可以看到WriteLock里面创建了一个Sync,Sync继承于AbstractQueuedSynchronizer,而AbstractQueuedSynchronizer其实就是一个Node的双向链表。
node存在多个状态
- CANCELLED,值为1,表示当前的线程被取消
- SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
- CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
- PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
- 值为0,表示当前节点在sync队列中,等待着获取锁。
lock的实现主要就是基于AbstractQueuedSynchronizer双向链表与AbstractQueuedSynchronizer Node状态的控制。具体流程网上有很多文章讲解,这里推荐几篇
AbstractQueuedSynchronizer源码解读
JUC锁框架_AbstractQueuedSynchronizer详细分析