多个线程操作相同变量时存在两个问题

一、竞态条件

当多个线程访问和操作同一个对象时,最终执行结果与执行时序有关,可能正确也可能不正确。

比如一个static int counter在多个线程中++时,每次输出的结果都不一样。因为counter++这个操作不是原子操作,它分为三个步骤:

  1. 取counter的当前值;
  2. 在当前值基础上加1;
  3. 将新值重新赋值给counter。

两个线程可能同时执行第一步,取到了相同的counter值,比如都取到了100,第一个线程执行完后counter变为101,而第二个线程执行完后还是101,最终的结果就与期望不符。

二、内存可见性

多个线程可以共享访问和操作相同的变量,但一个线程对一个共享变量的修改,另一个线程不一定马上就能看到,甚至永远也看不到。

public class VisibilityDemo {
	private static boolean shutdown = false;

	static class HelloThread extends Thread {
		@Override
		public void run() {
        	while(! shutdown){
         	   // do nothing
        	}
        	System.out.println("exit hello");
    	}
	}
	public static void main(String[] args) throws InterruptedException {
    	new HelloThread().start();
    	Thread.sleep(1000);
    	shutdown = true;
    	System.out.println("exit main");
	}
}

在这个程序中,有一个共享的boolean变量shutdown,初始为false, HelloThread在shutdown不为true的情况下一直死循环,当shutdown为true时退出并输出"exit hello",main线程启动HelloThread后休息了一会儿,然后设置shutdown为true,最后输出"exit main"。

期望的结果是两个线程都退出,但实际执行时,很可能会发现HelloThread永远都不会退出,也就是说,在HelloThread执行流看来,shutdown永远为false,即使main线程已经更改为了true。

这是怎么回事呢?

这就是内存可见性问题。在计算机系统中,除了内存,数据还会被缓存在CPU的寄存器以及各级缓存中,当访问一个变量时,可能直接从寄存器或CPU缓存中获取,而不一定到内存中去取,当修改一个变量时,也可能是先写到缓存中,稍后才会同步更新到内存中。在单线程的程序中,这一般不是问题,但在多线程的程序中,尤其是在有多CPU的情况下,这就是严重的问题。一个线程对内存的修改,另一个线程看不到,一是修改没有及时同步到内存,二是另一个线程根本就没从内存读。

#java原理##并发编程#
Java编程原理 文章被收录于专栏

知其然知其所以然,只有了解了底层原理,借助第一性原理,才可以运用自如,成为真大师。 什么是第一性原理? 第一性原理最早由亚里士多德提出,他将其定义为:“事物被已知的第一项前提。” 简单来说,它要求你不要用“类比”去思考(即:因为别人这样做,或者以前这样做,所以我也这样做),克服从众心理(FOMO)和经验偏差,在科技创新、商业决策中找到成本与效率的最优解。

全部评论
冲鸭,线程问题搞定!
点赞 回复 分享
发布于 今天 21:16 四川

相关推荐

暑期实习什么时候投?
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务