文章目录
在《并发编程(一)》的文末提到了多线程的三大特性:原子性,可见性和有序性。本篇针对原子性展开。
一、原子性
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区域。以前存在方法区的类信息,现在存到了本地内存的元数据区。
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();
........
}
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; //锁重入次数
_object = NULL;
_owner = NULL;//当前持有锁的线程
_WaitSet = NULL;//处于wait状态的线程,被加入到这个linkedList
_WaitSetLock = 0 ;//保护WaitSet的一个自旋锁
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL