线程安全性保证---JMM特性详解

概述

JVM
 Java虚拟机,运行程序的实体是线程,每个线程创建时JVM都会为其创建一个工作内存(称作栈空间)
JMM
 Java内存模型,是一种抽象的概念,描述的是一组规范,通过这组规范定义了;程序中各个变量(包括实例字段,静态字段,构成数组对象的元素)的访问方式

JMM关于同步的规定

1、线程加锁前,必须读取主内存的最新值到自己的工作内存
2、线程解锁前,必须把共享变量的值刷新回主内存
3、加锁解锁是同一把锁

JMM三特性

可见性:主物理内存的值只要被修改,其他线程马上会获取通知的机制

原子性:某个线程正在做某个具体业务时,不允许加塞或者被分割,需要整 体完成,要么同时成功,要么同时失败

有序性:经过指令重排,字节码指令的顺序可能与我们源代码顺序不一致,处理器在指令重排时必须要考虑数据之间的依赖性
1 > 单线程中最终指令运行结果和程序顺序执行结果一致,
2 > 在多线程中结果无法预测,所以必须保证线程执行的有序性
在这里成的撒描述

可见性、原子性、有序性共同保证了线程的安全性

线程对变量的操作三部曲

在这里插入图片描述

1 > 将变量拷贝到自己的工作内存
2 > 对变量进行修改
3 > 修改完成后再将变量写回主内存
原因 :
 工作内存是每个线程的私有数据区域,JMM规定所有变量都存储在主内存,主内存是共享区域,所有线程都可以访问,但线程对变量的操作必须在自己的工作内存中进行
注:
 i++不是原子性操作
 不能直接操作主内存,所以线程要互相进行通信,必须通过主内存进行

缺少可见性控制(写不可见)
public class Main implements Runnable{
	boolean f;
	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			f=true;
		} catch (Exception e) {
		}
		
	}
	public static void main(String[] args) {
		Main m=new Main();
		Thread t=new Thread(m,"A");
		t.start();
		while(!m.f) {}
		System.out.println("f为true,线程A将f修改成功");
	}
}

线程调入死循环,一直无输出
分析:f的可见性未得到保证,线程A虽修改成功,但主线程并不知道

缺少原子性控制(写丢失)

public class Main implements Runnable{
	volatile int sum;
	@Override
	public void run() {
		for(int j=0;j<200;j++) {
			sum++;
		}
	}
	public static void main(String[] args) {
		Main m=new Main();
		for(int i=0;i<20;i++) {
		   Thread t=new Thread(m);
		   t.start();
		}
		while(Thread.activeCount()>2) {}
		System.out.println(m.sum);
	}
}
结果分析:sum<=4000
原因:sum++不是一个原子性操作,并且Volatile无法保证原子性

缺少有序性控制(指令重排)

public void M() {
	int x=1;//语句1
	int y=2;//语句2
	x=x+5;//语句3
	y=x*x;//语句4
}
执行结果顺序可能是123413242134
语句4可能为第一条执行吗?  不可能,因为y依赖于x,存在数据间的依赖性
在单线程下不影响结果正确性
int x=1;
boolean f;
public void m1() {
    x=1;
   	f=true;
}
public void m2() {
	while(!f) {
		x+=5;
		System.out.println(x);
	}
}
//线程1执行m1,线程2执行m2
输出结果为65(线程1执行m1发生了指令重排)
多线程下导致输出结果不一致

总结:

  1. 更改不可见(可见性):
    主物理内存的值只要被修改,其他线程马上会获取通知的机制
  2. 写丢失(原子性):
    多个线程同时拿到同一资源,线程1将资源做了修改,此时线程2在获取 资源更改的通知前(可见性),也修改了资源,导致线程1修改丢失,出现了写覆盖情况
  3. 执行乱序(有序性):
    指令重排在编译器认为一种优化,但却使我们的程序在多线程中的数据一致性无法保证,所以
    执行乱序解决方案:
    synchronized:每次只有一个线程进入关键区,单线程指令重排不存在问题
    volatile:禁止指令重排
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值