|Java开发之并发编程的三要素解析

|Java开发之并发编程的三要素解析

文章图片


Java开发之并发编程的三要素解析
之前说完了计算机的内存模型 , 本篇我们再来简单了解一下并发编程的概念 , 也就是多线程情况下的编程 。
在并发编程中 , 我们最需要关心的是以下三个特征:原子性、可见性、有序性 。
1.原子性原子是一个化学名词 , 它是化学变化中最小的粒子 。 用在程序中 , 指一个操作或多个操作具有原子性时 , 它必须是要么全部执行 , 要么全不执行 。 没有执行一半的说法 , 也没有被执行一半时被打断的说法 。
它在一些场合下十分重要 , 就如同银行转账 , 如果我的账户扣了钱 , 那么对方的账户肯定是要涨了钱的 。 不可能出现我的钱扣了后 , 程序中断或被打断 , 导致对方没有涨钱这种可能 , 这会产生十分严重的后果 。
当然 , 也不只是程序中断才会影响结果 , 并发情况下 , 多个线程同时操作一个变量 , 也可能会出问题 。 就像我在一台ATM转出100 , 另个人同时向我转入100 , 他们同时读取了我的旧余额500 。 以此为基础作运算 , 我的余额就是共享变量 。 如果这段操作不具有原子性 , 那么我这正准备将转账出去后的400设置回余额 , 另一边又准备将转过来后的600设置为余额 , 同时操作 , 我的钱不是变成600就是变成400 , 但是其实哪个都不是正确的值 。
2.可见性可见性是指当多个线程访问同一个变量时 , 一个线程修改了这个变量的值 , 其他线程能够立即看到修改的值 。
没有可见性正如没有协议前的高速缓存 , 一个线程明明修改了共享变量 , 可是其他线程就是不知道这点 , 所以就得到了错误的结果 。

3.有序性然后就是有序性了 , 它指的是程序执行的顺序按照代码的先后顺序执行 。
正常情况下 , 这是没什么可说的 , 但是如果在Java中 , 并且是并发情况 , 就不得不说了 。
Java是有一种叫指令重排(Instruction Reorder)的操作的 , 这里只简单的说一下 , 详细的可以自行去了解 。
一般来说 , 处理器会为了提高程序运行效率 , 自动对代码进行优化 , 这个优化无法保证程序中的语句执行的先后顺序和代码先后顺序一致 , 但是 , 它可以保证程序最终执行的结果和代码顺序执行的结果一致 。
简单说 , 就是Java可能会对你代码的执行顺序进行调整 , 导致顺序不同 , 但是它能保证最终执行的结果是一样的 。
之所以能保证结果相同 , 是因为考虑到了数据依赖性 , 如果是两个没有数据依赖的指令 , 例如a=1 , b=2 , 那么谁先谁后显然是没有影响的 , 就可能会发生指令重排 。 但如果是a=1 , b=a , 第二个指令依赖了数据a , 这时就会保证第二个指令必须在第一条后面执行 , 保证了它的顺序 。
当然 , 这也只是在单线程的情况不会出问题 。 如果是并发情况就不一定了 。
例如这是一个简单的例子 , 目的是在a运算后等于2时输出结果 。
线程1执行:a=1+1aIs2=true线程2执行:if(aIs2){
\tSystem.out.println(a);


其中a=1+1和aIs2=true这两句之间没有数据依赖 , 也就是可能发生指令重排 。 如果是单线程 , 无论这两个顺序咋变 , 都不会影响最终结果 。
但如果是多线程 , 当线程1的先执行了指令重排后的aIs2=true , 还没有执行a=1+1 。 这时线程2开始执行了 , 由于aIs2=true , 导致它以为数据已经运算过了 , 直接进行输出 , 结果a却是未定义的值 。 这时候线程1才执行到a=1+1 , 可惜为时已晚 。