volatile关键字

volatile概念:
volatile关键字的主要作用是使变量在多个线程间可见。

public class RunThread {
	/**volatile**/
	private boolean isRunning = true;
	
	public void setRunning(boolean isRunning){
		this.isRunning = isRunning;
	}
	
	public void run(){
		System.out.println("进入run方法...");
		while(isRunning){
			//...
		}
		System.out.println("线程停止...");
	}
	
	public static void main(String args[]) throws InterruptedException{
		final RunThread rt = new RunThread();
		
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				rt.run();
			}
		});
		t1.start();
		Thread.sleep(3000);
		rt.setRunning(false);
		System.out.println("isRunning的值已经被设置为false");
		Thread.sleep(1000);
		System.out.println(rt.isRunning);
	}
}

输出结果:

进入run方法...
isRunning的值已经被设置为false
false

分析:
看似没有问题,但实际运行中,java主程序并没有结束,就说明了t1线程中rt的run方法仍然还在while的死循环里,而我们在主线程中已经把isRunning设置成了false,而且还输出了rt中isRunning的值,也确实是false,为啥rt的run方法没有跳出来呢?原因是jdk会为每一个线程中加一个独立的运行内存空间,在t1线程的这份独立运行内存空间,会装入t1线程将要用到的主内存的变量,即isRunning这个变量,t1线程会把这个副本拷贝到自己的独立运行空间,在t1线程运行过程中只会在这个副本里取这个值,目的就是为了提高线程的执行效率,而不用每次使用主内存变量的时候都要跑到主内存里去取值。

  • 加了volatile后:
    在这里插入图片描述
    输出结果:
进入run方法...
isRunning的值已经被设置为false
线程停止...
false

分析:
isRunning变量被volatile修饰后,会在isRunning变量改变时强制线程执行引擎去主内存里去读取。变量加了volatile后,也不会让t1线程每次去主内存取isRunning的值,t1线程任然会在自己赋值过来的副本里取值,只是isRunning变量改变时,把它加载到t1线程的独立运行空间里。
总结:
在java中,每一个线程都会有一块工作内存区,其中存放着所有线程共享的主内存中的变量值的拷贝。当线程执行时,他在自己的工作内存中操作这些变量。为了存取一个共享的变量,一个线程通常先获取锁定并去清除他的内存工作区,把这些共享变量从所有线程的共享内存中正确的装入到她自己所在的工作内存区中,当线程解锁时保证该工作内存中变量的值写回到共享内存中。
一个线程可以执行的操作有:使用(use),复制(assign),装载(load),存储(store),锁定(lock),解锁(unlock)。
而主内存可以执行的操作有读(read),写(write),锁定(lock),解锁(unlock),每个操作都是原子的。
在这里插入图片描述


从上面可知,volatile修饰的变量具有可见性,但volatile关键字不具备原子性,具体参考https://www.jianshu.com/p/cf57726e77f2

原子性:
原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。

public class VolatileNoAtomic extends Thread{
	private static volatile int count;
	private static void addCount(){
		for(int i = 0;i<1000;i++){
			count++;
		}
		System.out.println(count);
	}
	public void run(){
		addCount();
	}
	public static void main(String args[]){
		VolatileNoAtomic[] arr = new VolatileNoAtomic[10];
		for(int i = 0;i<10;i++){
			arr[i] = new VolatileNoAtomic();
		}
		for(int i = 0;i<10;i++){
			arr[i].start();
		}
	}
}

输出结果:

2000
5000
4000
3000
2000
6710
7701
6994
6881
8701

分析:
如果volatile关键字能保证原子性,则输出结果应该是10000,而每次运行结果都是小于等于10000。count++的操作过程也并不是一个原子性的操作,它经历了三个过程:1.读取count的值;2.对count+1;3.将新值复制给count。在此过程中如果线程A读取count到工作内存后,其他线程对这个值已经做了自增操作后,那么线程A的这个值自然而然就是一个过期的值,因此,总结果必然会是小于10000。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值