我是靠谱客的博主 眼睛大胡萝卜,最近开发中收集的这篇文章主要介绍java频道360,Java并发编程看这一篇就够了!!!,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、前言

七月的天气分外炎热,心中燥意难以抚平,决定梳理下之前落下的笔记。正好最近对JMM比较感兴趣,就整理出了这篇文字。

二、序言

即使你的程序没有显示的创建任何线程,框架也可能为你创建了一些线程,这些线程调用的代码必须是线程安全(thread safe)的。这一点给开发人员的设计和实现赋予了更重要的一分责任。

本文上下两篇,主要通过图文结合的方式描述了如下五个方面,希望有所帮助:

Java内存模型

指令重排序

顺序一致性模型

volatile内存语义与实现

锁内存语义与实现

三、正文

1. Java内存模型

在并发编程中,需要处理两个关键的问题:线程之间如何 通信及线程之间如何同步。

通信

通信是指线程之间以何种机制来交换信息。在命令式编程语言中,线程之间的通信机制有两种方法:共享内存和消息传递。

共享内存

这种并发模型中,线程之间通过写-读内存中的公共状态来进行隐式通信。Java底层就是采用的这种方式。

消息传递

这种并发模型中,线程之间没有公共状态,线程之间必须通过发送消息来显示进行通信。如:流行的Actor模型。

Java线程之间的通信由Java内存模型控制(简称JMM),JMM决定一个线程对共享变量的写入,何时对另一个线程可见。

548413f9430f4c1a9a01d3d01f06210a.png

JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个本地内存(Local Memory),本地内存中存储了该线程读/写共享变量的副本。

由此可见,线程A与线程B要通信的话,必须经历2个步骤。

a.线程A将本地内存中的副本刷写到主内存中去。

b.线程B从主内存读取已被A更新过的共享变量到B的本地内存

那么,只要有其中一个环节没有完成,线程间的可见性就得不到保障,造成数据的不准确。

2. 指令重排序

2.1 什么是指令重排序?

在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。

a7c018b29a64309459c3612b41d67880.png

2.2 指令重排序的条件

2.2.1 数据依赖性

如果两个操作访问统一个变量,且其中一个为写操作,那么这两个操作之间就存在数据依赖性。

7523c8307384aaf2b9666faf7f47c044.png

重排序在以上三种情况下不会触发。

2.2.2 as-if-serial语义

as-if-serial语义的意思是:不管怎么重排序,(单线程)程序的执行结果不能被改变。

我们通过一个计算圆面积的例子,说明这个问题:

ed2de4046c3effe06ab4181f535f7b94.png

可以看到,无论如何重排序,程序的结果是不变的,准确的。

以上,我们讨论了单线程情况下重排序的影响。(好像与我们写程序期望的目标一致: P),对于多线程程序,就未必如此了。

我们也是通过一个简单的程序来说明该影响:

假设有两个线程A和B,A先执行writer方法,然后B执行reader方法。 我们可以分析得到,这两个方法内的代码都不符合数据依赖性原则,因此都有可能被重排序。

重排序造成的执行路径有很多种,我们这里拿出其中的两种来说明问题。

33c7cae3ed7a6043745300ce9c0e07b1.png

读取到了未赋值的a变量

f680b166fb07e2ea0f943d703bedc6e8.png

说明:int i = a * a;

这个操作可以分成两个步骤,首先计算 a * a,接着赋值给变量i。

大家可以在脑中计算下这两种情况下变量i的值。显然,都不符合我们写代码期望的结果。我们初步认识到了,重排序对于多线程情况下的可能造成的负面影响。

3. 顺序一致性模型

上面我们初步认识到了重排序可能对多线程的负面影响,那么如何避免此种情况的发生呢?

我们下篇接着讨论: D.

推荐阅读

最后

以上就是眼睛大胡萝卜为你收集整理的java频道360,Java并发编程看这一篇就够了!!!的全部内容,希望文章能够帮你解决java频道360,Java并发编程看这一篇就够了!!!所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(92)

评论列表共有 0 条评论

立即
投稿
返回
顶部