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

一、竞态条件

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

比如一个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知识专辑 文章被收录于专栏

知其然知其所以然,只有掌握了底层原理,借助第一性原理,才可以在日常开发和项目中运用自如,潇洒走江湖。

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

相关推荐

这几天在牛客上找别人的简历观摩,从一开始的焦虑、迷茫,到现在逐渐冷静下来。​有一句评论说得很对:“其实大家项目做的都差不多,就看谁理解更深”。我一直在想:怎样才能做出一个新颖的项目?怎样才能github拿star?怎样去做一个工具类的而不是烂大街的项目? 出发点虽然很好,但却不符合我当下所处的社会需求。一是时间不够,好的项目需要持久的沉淀与细心雕琢,并且通常是少有明确功利心的;二是字数不够,这些岗位无法通过一张A4纸的内容就看明白一个人擅长什么,更无法通过区区几行就评判一个项目是流水账还是经得起推敲;三是要求过高,实际上简历不过是一页纸张而已,可却有太多人过于重视其含义。坦诚来看,简历是一个工具,通过它来让公司对求职者感兴趣。我相信他们更在意的是一个人能通过那些项目经历能体现他掌握哪些知识,而非这个项目有多罕见、多出名。所以没有必要在一个充当过渡期的项目上花费过于多的时间,而是去深究从中能理解哪些技术才是。​我们都应当拥有这样一种心态:先做一个垃圾出来。这其中有着鼓励行动的暗示,不要太纠结做什么,先行动起来。这一以过程为导向的学习,我称为进步。我无意每日对比别人,自己是大佬还是彩笔,这样至少能够减少一份焦虑,做好控制内的事情。
点赞 评论 收藏
分享
03-02 19:46
门头沟学院 Java
急急急急急!以下为岗位jd职位名称: 测试开发工程师-实习生(网约车核心业务)工作职责:深度质量保障: 独立负责滴滴网约车核心链路测试工作,主导测试策略制定、测试方案设计与执行,确保项目高质量上线。效能平台/工具建设: 针对复杂的测试场景,主动识别效率瓶颈,设计并开发测试工具、框架或平台(如自动化测试平台、流量回放工具、精准测试系统等),提升研发测试整体效能。自动化体系构建: 负责构建和维护模块级、系统级的自动化测试用例,并推动在CI/CD流水线中落地,持续提升项目的自动化覆盖率和测试效率。前沿测试技术探索: 深入探索并实践业界先进的测试方案(如智能化测试、混沌工程、立体化监控等),为业务提供更可靠、更高效的质量保障。任职要求:2027年及以后毕业的在校本科生或研究生,可保证每周实习不少于4天,能连续实习3个月以上者优先。熟悉Python/Golang/C++等至少一门编程语言,具备良好的编码能力,有实际项目开发经验者优先。具备强烈的责任心和 owner意识,逻辑清晰,具备优秀的问题分析和解决能力。(加分项)对软件测试有浓厚兴趣,了解自动化测试、性能测试或测试工具开发者优先。我们提供:核心业务: 深度参与千万级用户产品的研发,技术挑战与成长空间巨大。优秀团队: 扁平化管理,技术氛围浓厚,有导师一对一指导。有竞争力福利: 餐补、交通补贴、团队团建等。联系方式:jiyelin@didiglobal.com / huyuhan@didiglobal.com
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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