一.线程的抢占式执行
我们发现,我们创建两个线程分别执行add方法50000次,理想状态下count最后应该是100000才对,但是从打印的结果看,不仅相差甚远,而且每一次的结果都不相同,这又是为什么呢?
原子性问题:一个操作可以被拆分成多步实现,比如上面的count++(load,add,save)
解决方法
加锁:synchronized
其原理就是当一个线程调用这个方法时,就会获取锁对象,当其他线程也要调用这个方法时,就会发生阻塞,简单来说就是把你拒之门外,因为你门被反锁了,只有当门里面的人出来(释放锁对象),此时才可以重新调用这个方法
二.内存可见性
当一个变量被内存重复读的时候,如果一段时间内没有发生改变,此时可能会触发编译器的优化机制,直接从寄存器中读取数据,读取速度非常快,以至于当我们以后改变此值时,编译器还是以原来的值读
解决方法
volatile:保证内存关键性,在写入volatile修饰的变量时,改变工作内存中变量副本的值,且将改变的值刷新到主内存中,在读入volatile修饰的变量时,将最新的值刷新到工作内存中,工作内存读取变量副本的值。
三.指令重排序
以单例模式中的懒汉模式为例
这里不仅有读操作,而且还有写操作
思考一个问题:创建对象过程经过了哪些步骤
1.申请内存空间
2.调用构造方法,将这块空间分配给对象
3.将内存空间的地址赋值给lazy引用
在正常情况下,编译器都是按照123顺序执行的
但是,又是由于提高效率----->编译器优化,可能会出现132的顺序情况
如果按照132顺序来进行
当线程1执行完1,3后,被cpu切了出去,这时候线程2进来,由于此时lazy引用已经被赋值了,不为null,所以线程2直接返回,但是此时的对象还没有构造完,是一个非法的对象,问题也就此产生了
解决方法
还是volatile:
故volatile不仅可以解决内存可见性问题,而且还可以解决指令重排序问题