java synchronized与lock的区别

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详细分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值