【多线程】变量可见性&线程安全

(一)并发中的变量可见性问题

1.变量分类?

全局变量
局部变量

属性(静态的、非静态的) 全局变量
本地变量 局部变量
参数 局部变量

2、如何在多个线程间共享数据?

用全局变量:静态变量,或共享对象

3.一个变量在线程1中被改变值了,在线程2中能看到该变量的最新值吗?
import java.util.concurrent TimeUnit
public class VisbilityDemo{
	//状态标识
	private static  boolean is=true;
	Public static void main(String[] args){
		new Thread(new Runable(){
			public void run(){
				int i=0;
				while(VisibilityDemo.is) {
					i++;
				}
				System.out.println(i);
			}
		}),start();

		try {
				TimeUnit.SECONDS.slee(2);
		}catch (InterruptedException e){
			e.printStackTrace();
		}
		//设置is为false,使上面的线程结束while循环
		VisibilityDemo.is = false;
		System.out.println("被设置为false了");
	}
}
线程会停止循环,打印出i的值吗?不会

*并发的线程能不能看到变量的最新值,这就是并发中的变量可见性问题

怎样才能可见?
方式一:同步关键字:synchronized

在这里插入图片描述

方式二:volatile

在这里插入图片描述

(二)变量可见性、线程安全问题原因

1.Java内存模型
Java内存模型以及操作规范变量

1.共享变量必须存放在主存中;
2.线程有自己的工作内存,线程只可操作自己的工作内存;
3.线程要操作共享变量,需从主存中读取到工作内存,改变值后需从工作内存同步到主存中。
在这里插入图片描述

Java内存模型会带来什么问题?

*有变量A,多线程并发对其累加会有什么问题?如三个线程并发操作变量A,大家读取A时读到A=0,
都对A+1,在将值同步会主内存,结果是多少?
结果是1.

这就是线程安全问题。

如何解决线程安全问题?

内存模型也产生了变量可见性问题。

如何让线程2使用A时看到最新值?

1.线程1修改A后必须马上同步回主内存
2.线程2使用A前需从主内存读取到工作内存。

疑问一:使用前不会重新从主内存读取到工作内存吗?
疑问二:修改后不会立马同步会主内存吗?

2.Java内存模型–同步交互协议

在这里插入图片描述

规定了8种原子操作:

1.lock(锁定):将主内存中的变量锁定,为一个线程所独占
2.unlock(解锁):将lock加的锁定解除,此时其它的线程可以有机会访问此变量
3.read(读取):作用于主存变量,将主存中的变量读到工作内存当中
4.load(载入):作用于工作内存变量,将read读取的值保存到工作内存中的变量副本中
5.use(使用):作用于工作内存变量,将值传递给线程的代码执行引擎
6.assign(赋值):作用于工作内存变量,将执行引擎处理返回的值重新赋值给变量副本
7.store(存储):作用于工作内存变量,将变量副本的值传送到主内存中
8.write(写入):作用于主内存变量,将store传送过来的值写入到主内存的共享变量中

操作规范:

1.将一个变量从主内存复制到工作内存要顺序执行read load操作;要将变量从工作内同步
回主内存要顺序执行store、write操作。只要求顺序执行,不一定是连续执行
2.做了assign操作,必须同步回主存。不能没做assign,同步回内存。
在这里插入图片描述

并发中保持变量可见性的方式:
1.final变量
 //final不可变变量
 private final int var1 = 1;
2.Synchronized
while (VisibilityDemo.is){
	synchronized(this){
		i++;
	}
}
3.用volatile修饰
//状态标识
private static volatitle boolean is =true;
1synchronized怎样做到可见性?
1synchronized语义规范:

1.进入同步块前,先清空工作内存中的共享变量,从主内存中重新加载。
2.解锁前必须把修改的共享变量同步回主内存

1synchronized如何做到线程安全的?

1.锁机制保护共享资源,只有获得锁的线程才可操作共享资源;
2.synchronized语义规范保证了修改共享资源后,会同步回主存,就做到了线程安全。

(三)volatile关键字解密

1.volatile怎样做到可见性
volatile语义规范:

1.使用volatile变量时,必须重新从主内存加载,并且read 、load是连续的。
2.修改volatile变量后,必须立马同步回主内存,并且store、 write是连续的。

volatile能做到线程安全吗?

1.不能,因为它没有锁机制,线程可并发操作共享资源。

在这里插入图片描述

1为什么使用volatile?

synchronized可以保证可见性,为什么要用volatile?
1.主要原因:使用volatitle比synchronized简单
2.volatile性能比synchronized稍好。
*如果是保证可见性,用volatile;保证线程安全用synchronized。

volatile还有什么用途?

volatile可用于限制局部代码指令重排序
1.线程A

content = initCoutent(); //(1)
islint = true;//(2)

线程B

is(islint){//(3)
    content.operation();//(4)
}

2.jvm优化指令排序后,代码的执行顺序可能如下:
线程A

islint = true;//(1)
content = initCoutent(); //(2)

3.当两个线程并发执行时,就可能出现线程B中抛空指针异常。
4.当我们在变量上加volatile修饰时,则用到该变量的块中就不会进行指令重排优化。

volatile的使用场景
volatitle的使用范围:

1.volatile只可修饰成员变量(静态的、非静态的)【全局变量】
2.多线程并发下,才需要使用它。

volatitle典型的应用场景:

1、只有一个修改者,多个使用者,要求保证可见性的场景
2、状态标识,如示例中介绍的标识
2、数据定期发布,都个获取者

在这里插入图片描述
volatile保证并发的情况下,不需要太多的线程进入到锁机制

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值