并发编程(二)原子性和Synchronized同步锁

在《并发编程(一)》的文末提到了多线程的三大特性:原子性,可见性和有序性。本篇针对原子性展开。

一、原子性

1.什么是原子性

一个操作(事务),不管分几步完成,最终结果只有两种可能,所有步骤都成功或者都失败。

举例:i++操作有三步
①从内存读i的值
②i的值+1
③把结果写回内存(如果阻塞,则修改的值无法写回内存)

在这里插入图片描述

结论:非原子操作

2.造成原子性的原因

①cpu高速缓存的不可见性
在这里插入图片描述
两个线程并行执行 i-- 操作,正确结果应该为8,运行结果为9。

②上下文切换造成的阻塞

比如:
硬件中断(鼠标和键盘操作)
网络/O就绪
线程阻塞,等待

3.解决原子性问题之Synchronized同步锁

Synchronized是一个关键字,用Synchronized修饰的内容(加了锁,并行变串行),在同一时刻只允许一个线程访问。具有排他性

二、Synchronized

1. Synchronized的使用

在这里插入图片描述

Synchronized位置

①② 修饰方法头,如果同时修饰静态方法(static),作用域是该类的所有实例对象。
③④⑤修饰方法体({}包裹的内容),()号里的内容就是作用域的范围。

类名.class作用域也是该类的所有实例对象。
其它使用方法的作用域为同一个对象实例。

根据lock_flag来判断锁的状态,0表示无锁状态,1表示有锁状态。

结论:①锁的标记或竞争依赖于对象 ② 锁的本质:竞争共享资源

2. Synchronized的原理
2.1、JVM的结构。

JDK1.8取消了方法区和永久代,转而在内存中开辟了一个元数据区来存储类的信息,字段,方法,接口等,如下图的Meta区域。以前存在方法区的类信息,现在存到了本地内存的元数据区。
jdk 1.8以后,

2.2、类的加载,方法的执行

在这里插入图片描述
方法进栈,对象进堆。
每个栈元素中存放了对应方法的局部变量,操作数栈,动态链接,返回地址等信息。
如上图,mian()方法栈中存放了局部变量h-------->h指向堆中的Hello类实例对象h---------->实例h中的元数据指针指向元数据空间中的Hello类的信息。

2.3、对象头MarkOop

在这里插入图片描述
在hotspot源码中,一个oop实例的构造如下(路径…\src\share\vm\oops\oop.hpp)

class oopDesc {
   
  friend class VMStructs;
 private:
  volatile markOop  _mark; //对象头
  union _metadata {
   
    Klass*      _klass; //元数据指针
    narrowKlass _compressed_klass; //压缩指针
  } _metadata;
........
........
 // locking operations
  bool is_locked()   const;
  bool is_unlocked() const;
  ........
  ........
   // garbage collection
  bool is_gc_marked() const; //是否被gc标记
  ........
  // Age of object during scavenge
  uint age() const;  //gc年龄
  void incr_age();
   ........
   ........
  // identity hash; returns the identity hash key (computes it if necessary)
  // NOTE with the introduction of UseBiasedLocking that identity_hash() might reach a
  // safepoint if called on a biased object. Calling code must be aware of that.
  intptr_t identity_hash();//hashcode
  intptr_t slow_identity_hash();
  ........
}

hostspot教程

markOop对象头: 对象的状态信息
①hashCode:对象在内存中的存放地址
②gc年龄:经历的gc次数(4bit最多15次)
③锁标记:是否持有锁
④monitor:锁监视器

_klass 类元数据指针: 指向本地内存的元数据区,表示该对象是哪个类的实例;

实例数据: 实际数据,变量等,如果变量是数组,则存储的是数组的指针;

对齐填充: 64位的cpu读取内存数据,一次读8个byte(8个内存块)
以前,分次读取,合并计算--------> 为了避免资源浪费,不足8个字节的部分会填充成8个字节,再读取;(空间换时间)

2.4、monitor锁监视器

路径:…\src\share\vm\runtime\objectMonitor.hpp

class ObjectMonitor {
   
............
//构造方法,实例化
ObjectMonitor() {
   
    _header       = NULL; //对象头(markOop对象)
    _count        = 0; //抢占该锁的线程数量
    _waiters      = 0,//当前处于等待状态的线程数量
    _recursions   = 0; 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值