根据java内存模型理解并发出现的问题

原子性

1、某些读写共享变量的操作如果不是原子操作,多线程并发的情况下会出现并发问题。
2、原子性实现了多个线程并发访问某段代码的时候,使这些线程能够有序访问。因为实现原子操作代码的一旦被执行,就不能被打断,其他线程想要访问的时候,只能阻塞等待。
3、java中实现原子性使用了synchronized关键字,在synchronized块之间的代码具备原子性。

可见性

并发问题并不只是由于原子性导致的;
1、组成原理中学过,为了更充分的利用CPU的性能,往往要在内存与处理器之间加一层:Cache(缓存),来作为内存与处理器之间的缓冲:将处理器需要的数据复制到缓存当中,当运算结束后再从缓存同步回内存当中。因为缓存的速度远远快于内存,这样处理器无需等待缓慢的内存读写,解决了处理器与内存的速度矛盾。
2、Java虚拟机也有类似的机制,每个线程有其自己的工作内存(类似前面的Cache),线程对变量的读写必须在工作内存中进行,而不能直接读写主存中的变量。(这里的变量指被各个线程共享的变量,比如堆中的对象和方法区中的变量。),如图:
《根据java内存模型理解并发出现的问题》

3、这样的机制会带来另一个问题:缓存一致性。多个线程共同处理同一个变量时,各自的缓存中的数据并不一致,同步回主内存的数据以谁的缓存数据为准呢?这就带来了并发问题。
4、即使我们使对共享变量的写操作实现了原子性,但由于内存可见性的问题,依然存在并发问题。这就是造成多线程并发的第二个原因:内存可见性。
5、使用synchronized关键字可以保证不存在并发问题,是因为synchronized不仅实现了代码原子性操作,还保证了内存可见性。每次执行加锁和释放锁的同时,都会把线程的工作内存和主内存进行同步。即在synchronized块内的操作:如果进行写操作,会将工作内存中的内容同步到主内存;进行读操作,会从主内存中同步到工作内存;

有序性

1、Cpu在执行指令的时候,为了优化提高Cpu运行程序的速度,会将多条指令不按程序规定的顺序分发给各个不同的电路单元处理,叫做指令重排序。注意乱序执行的指令之间没有数据依赖关系,因为乱序执行的结果必须保证结果的正确性;
比如:2个不同线程中的代码执行是无序的–会进行重排序;
2、指令重排序也是造成并发问题的一个因素;
3、synchronized关键字也可以解决指令重排序带来的并发问题,他可以保证线程之间操作的有序性。如果使用synchronized关键字将上面例子中访问initialized的相关代码包裹起来,就保证了这种多线程之间操作的有序性。因为使用synchronized关键字后,持有同一个锁的两个同步块只能串行的进入;

总结

造成多线程并发问题的三个原因:原子性、可见性、原子性;
通过synchronized可以解决这三个因素带来的并发问题,所以java中并发控制能通过synchronized来实现;

    原文作者:java内存模型
    原文地址: https://blog.csdn.net/bh_xiaoxinba/article/details/52518091
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞