由于之前看的容易忘记,因此特记录下来,以便学习总结与更好理解,该系列博文也是第一次记录,所有有好多不完善之处请见谅与留言指出,如果有幸大家看到该博文,希望报以参考目的看浏览,如有错误之处,谢谢大家指出与留言。
一、原子性
原子性是指一个操作是不可中断的。即使是在多线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
一般认为CPU指令是原子性的,要么不执行,要么全部执行完。
但写的程序就不一定,比如:i++ 至少包含两个操作,读和加两个操作,其实还有加完写到i中。所以这中间多个线程同时操作,就可能数据混乱。
二、有序性
在并发时,程序的执行可能就会出现乱序。
我们一般认为指令是一条一条执行的,但其实一条指令的执行是可以分为很多步骤(这里的指令是的是汇编语言的指令),如下:
如下两条指令,如果分解成不成阶段(五个阶段),假如每一个阶段消耗一个cpu时钟周期,如果串行的方式执行,会消耗10个cpu时钟周期,所以,在一条执行时,当实行完if时,他的处理if指令的硬件是空闲,那么下一条就可以跟着同时执行,像流水线一样执行。如下图:A=B+C案例
中间在add的时候有个X阶段,是指在上一步MEM执行完后才能执行EX,因为X的时候R2值还没有,所以没法相加,当MEM步骤时就可以从硬件直接获取,不必等到写回到寄存器WB步骤。X其实可以理解为气泡。最后一步为什么还有一个气泡的原因是,如果去掉气泡EX前移会跟第三步中的EX重合相冲突,因为同一个操作,在两个不同指令之间不可以同时操作,会访问同一个硬件设备。
下面这个案例是,消掉气泡,操作。穿插操作,产生气泡原因是,LW步骤和ADD是依赖关系,那么在中间去操作另一个指令,就可以消掉气泡。提高了,气泡带来消费问题。因此这就是指令重排。
只要没有依赖关系,都可以指令重排,因此多线程执行时,出现乱序问题,就是指令重排产生的。指令重排只是其中一种优化方式,还会有其他优化方式同时操作。
可见性原因是,各种性能优化产生的,编译器,虚拟机级别的优化造成的,主要虚拟机的指令重排优化,造成可见性
多线程安全问题可以使用同步块阻塞解决,或者一些非阻塞方式
三、可见性
可见性是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。
可见性是由于不同级别优化产生的,不同环节产生,可以看做系统性问题。
3.JVM层面的可见性:http://hushi55.github.io/2015/01/05/volatile-assembly
使用-server模式启动,因为他会执行更多的优化方案。-client主要是给用户,体验快。下面是打印执行步骤
介绍这么多可见性问题,主要是讲述,可见性问题成因比较复杂,产生的原因有好多种,基于不同程度优化方便导致的,总而言之是:可见性是一个线程中你可能看不到另一个线程对某个线程的修改。解决方法也比较简单,加个volatile。
下面也是表现可见性与指令重排现象:
可见性:http://www.techweb.com.cn/network/system/2017-06-24/2539856.shtml
四、Happen-Before(先行发生规则)
五、线程安全初识
指某个函数,函数库在多个线程环境中被调用时,能够正确的处理各个线程的局部变量,使程序功能正确完成。
下面就是不安全的操作举例:
解决线程安全也简单,通过阻塞的方式:
参考:http://www.techweb.com.cn/network/system/2017-06-24/2539856.shtml
学习:https://blog.csdn.net/ty_laurel/article/details/52403718
https://www.cnblogs.com/humc/p/5426351.html
http://ifeve.com/java%E9%94%81%E6%98%AF%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E6%95%B0%E6%8D%AE%E5%8F%AF%E8%A7%81%E6%80%A7%E7%9A%84/