synchronized:具有原子性,有序性和可见性
粒度:对象锁、类锁
三大性质总结
1. 三大性质简介
在并发编程中分析线程安全的问题时往往需要切入点,那就是两大核心:JMM抽象内存模型以及
happens-before规则,三条性质:原子性,有序性和可见性。关于synchronized和volatile已经讨论过
了,就想着将并发编程中这两大神器在 原子性,有序性和可见性上做一个比较,当然这也是面试中的高
频考点,值得注意。
2. 原子性
原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及
时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。我们先来看看哪些是原
子操作,哪些不是原子操作,有一个直观的印象:
int a = 10; //1
a++; //2
int b=a; //3
a = a+1; //4
上面这四个语句中只有第1个语句是原子操作,将10赋值给线程工作内存的变量a,而语句2(a++),实
际上包含了三个操作:1. 读取变量a的值;2:对a进行加一的操作;3.将计算后的值再赋值给变量a,而
这三个操作无法构成原子操作。对语句3,4的分析同理可得这两条语句不具备原子性。当然,java内存模
型中定义了8中操作都是原子的,不可再分的。
1. lock(锁定):作用于主内存中的变量,它把一个变量标识为一个线程独占的状态;
2. unlock(解锁):作用于主内存中的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可
以被其他线程锁定
3. read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便
后面的load动作使用;
4. load(载入):作用于工作内存中的变量,它把read操作从主内存中得到的变量值放入工作内存中
的变量副本
5. use(使用):作用于工作内存中的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚
拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作;
6. assign(赋值):作用于工作内存中的变量,它把一个从执行引擎接收到的值赋给工作内存的变
量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
7. store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送给主内存中以便随后
的write操作使用;
8. write(操作):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的
变量中。
上面的这些指令操作是相当底层的,可以作为扩展知识面掌握下。那么如何理解这些指令了?比如,把一
个变量从主内存中复制到工作内存中就需要执行read,load操作,将工作内存同步到主内存中就需要执行
store,write操作。注意的是:java内存模型只是要求上述两个操作是顺序执行的并不是连续执行的。也
就是说read和load之间可以插入其他指令,store和writer可以插入其他指令。比如对主内存中的a,b进
行访问就可以出现这样的操作顺序:read a,read b, load b,load a。
由原子性变量操作read,load,use,assign,store,write,可以大致认为基本数据类型的访问读写具备原子
性(例外就是long和double的非原子性协定)
synchronized
上面一共有八条原子操作,其中六条可以满足基本数据类型的访问读写具备原子性,还剩下lock和
unlock两条原子操作。如果我们需要更大范围的原子性操作就可以使用lock和unlock原子操作。尽管
jvm没有把lock和unlock开放给我们使用,但jvm以更高层次的指令monitorenter和monitorexit指令开
放给我们使用,反应到java代码中就是---synchronized关键字,也就是说synchronized满足原子性。
如果让volatile保证原子性,必须符合以下两条规则:
1. 运算结果并不依赖于变量的当前值,或者能够确保只有一个线程修改变量的值;
2. 变量不需要与其他的状态变量共同参与不变约束