线程安全之可见性

JAVA内存模型详解

JVM运行时数据区
来自网易云课堂
线程独占:每个线程都会有它独立的空间,随线程生命周期而创建和销毁
线程共享:所有线程能访问这块内存数据,随虚拟机或者GC而创建和销毁

java虚拟机规范:描述JAVA虚拟机规范 JVM
JAVA语言规范:java的语法 和语言特性
java内存模型由java语言规范提出,描述Java语言特性。
JVM运行时数据区由java虚拟机规范提出,描述java进程的内存情况

JAVA内存模型
一次执行单个语句或表达式,即通过通过单个线程来执行。JAVA虚拟机可以同时支持多个执行线程,
若为正确同步,线程的行为可能会出席那混淆和违反直觉。

当多个线程修改了共享内存中的值时,应该读取到哪个值的规范。由于这部分规范类似于不同硬件体系
结构的内存模型,因此这些语义称为JAVA编程语言内存模型。

多线程语义没有规定如何执行多线程程序。相反,它们描述了允许多线程程序的合法行为

多线程中的问题
1、所见非所得
2、无法用肉眼去检测程序的准确性
3、不同的运行平台有不同的表现
4、错误很难重现

package day04;

/**
 * 无法用肉眼去检测程序的准确性
 * 不同的运行平台有不同的表现 idea通过设置vm options 为 -client 可打印,elipse未找到对应调整方法
 * 参数		32位JDK			64位JDK
 *-server		不打印i的值		不打印i的值
 *-client		打印i的值		不打印i的值
 *可用 volatile 修饰  可见性
 * @author sy
 *
 */
public class Demo01 {
	long i=0;
	//volatile boolean isRunning = true;
	boolean isRunning = true;
	public static void main(String[] args) throws InterruptedException {
		Demo01 demo=new Demo01();
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("here i am....");
				while(demo.isRunning) {
					demo.i++;
				}
				System.out.println(demo.i);
			}
			}).start();
		
		Thread.sleep(3000L);
		demo.isRunning =false;
		System.out.println("shutdowm...");
	}
}

java内存模型 仅提出规则,并未真正的去解决问题 最终由jvm去实现

网易云课堂

CPU指令重排序
java变成语言的语义允许java编译器和微处理器进行执行优化,这些优化导致了与 其交互的
代码不再同步,从而导致看似矛盾的行为

JIT编译(Just in Time Compiler) 会进行性能优化,指令重排
				执行前编译(不会有性能优化)
java源代码 -->java编译器	---->字节码 ---》JVM
											1、解释执行
											2、编译执行(当方法被多次调用,或者方法中的循环体多次循环 就会上升为 编译执行) 即JIT 存至方法区

1、即以此执行单个语句或表达式,即通过单个线程来执行。java虚拟机可以同时支持多个执行线程,
若为正常同步,线程的行为可能会出现混淆和违反直觉
2、当多个线程修改了共享内存中的值时,应该读取到哪个值的规则。由于这部分规范类似于不同硬件体系的内存模型,
因此这些语义称为JAVA编程语言内存模型。
3、 这些语义没有规定如何执行多线程程序。相反它们描述了允许多线程程序的合法行为

脚本语言 解释执行:即脚本,在执行时,由语言的解释器将其 一条条翻译 成机器可识别的指令
编译语言 编译执行:将我们所编写的程序,直接翻译成机器可以识别的指令码

JAVA是 解释执行+编译执行
-client 不会优化,其他会 32为JDK
volatile
对volatile变量v的写入,与所有其他线程后续对v的读同步

1、禁止缓存 关键字
2、对volatile变量相关的指令不做重排序
由JVM具体实现 ACC_VOLATILE
在这里插入图片描述

Shared Variables 共享变量 可以在线程之间共享的内存称为共享内存或者堆内存
所有的实力字段、静态字段和数组元素都存储在堆内存中,这些字段和数组都是共享变量
冲突:如果至少有一个访问是写操作,那么对同一个变量的两次访问是冲突的

线程间操作的定义
1、线程间操作指:一个线程执行的操作可以被其他线程感知或者被其他线程直接影响
2、JAVA内存模型只描述线程间操作,不描述线程内操作,线程内操作按照线程内语义执行

线程间操作;
read操作(一般读,即 非volatile读)
write操作(一般写,即 非volatile写)
volatile resad
volatile write操作
Lock(锁 Monitor)、Unlock
线程的第一个和最后一个操作
外部操作
所有线程间操作,都存在可见性问题,JMM需要对其进行规范

对于同步的规则定义
对volatile变量v的写入,与所有其他线程 后续 对v的读同步
对于监视器m的 解锁 与所有后续操作对于m的 加锁 同步(加锁解锁不能指令重排,前一个线程的加锁解锁对后续线程的加锁解锁可见)

对于每个属性写入默认值(0,false,null)与每个线程对其进行的操作同步(默认值 指未赋值情行)
启动线程的操作与线程中的第一个操作同步
线程T2的最后操作与线程T1发现线程T2已经结束同步。(isAlive,join可以判断线程是否终结)
如果T1线程中断了T2线程,那么线程T1的中断操作与其他所有线程(包括T2)发现T2被中断了同步
	通过抛出InterrruptedException异常,或者调用Thread.interrupted或者Thread.isInterrupted

Happends_before 先行发生原则
用于描述两个有冲突的动作之间的顺序,如果action haapends before 另一个action,
则第一个操作被第二个操作可见,JVM需要实现如下happends_before 规则
1、某个线程中的每个动作都happends-before该动作后面的动作
2、某个线程上的unlock动作happends-before 同一个线程上后续的Lock操作
3、对某个volatile字段的写操作 happends-before 每个后续对该volatile字段的读操作
4、在某个线程对象上调用start()方法happens-before被启动线程中的任意动作
5、如果在线程T1中成功执行了T2.join(),则T2中的所有操作对T1可见(join 包含调用)
6、如果某个动作 a haapends-before 动作b,且b happends-before动作c,则有a haapends-before动作c

final在JVM中的处理
final在该对象的构造函数中设置对象的字段,当线程看到该对象时,将始终看到该对象的final字段的正确构造版本。
如果在构造函数中设置字段后发生读取,则会看到该final字段分配的值,否则将看到默认值

读取该共享对象的final成员变量之前,先要读取共享对象
同城被static final修饰的字段,不能被修改。然而System.in、System.out、
	System.err被static final修饰,却可以修改,遗留问题,必须允许通过set方法修改,
	将这些字段称为写保护,以区别于普通final字段

Word Tearing 字节处理
有些处理器(尤其是早期的Alphas处理器)没有提供写单个字节的功能。在这样的处理器上更新byte数组,
若只是简单的读取整个内容,更新对应的字节,然后将整个内容再写回内存,将是不合法的。

这个问题有时候被称为"字分裂(word tearing)",更新单个字节有难度的处理器,就需要寻求其他方式来解决问题。
因此,编程人员需要注意,尽量不要对byte[]中的元素进行重新赋值,更不哟啊哎多线程程序这样做。

double和long的特殊处理
由于<java语言规范>的原因,对非volatile的double、long的单次写操作是分两次来进行的,每次操作其中32位,
这可能导致第一次写入之后,读取的值是脏数据,第二次写完成之后,才能读到正确的值。
(这时加volatile并不是起同步作用,而是将这个修改变为原子性操作)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值