zoukankan      html  css  js  c++  java
  • 记一次多线程下使用while出现的问题

    问题概要

    我在研究volatile关键字应用场景的时候,无意中发现了while在多线程情况下出现了“非正常输出“的情况。

    问题分析

    这段代码的意义是创建两个线程,分别执行test1()方法和test2()方法,其中标识全局变量(也就是两个线程共享的变量)flag初始化为false;为了使步骤更加清晰可见,我将test1()的初始化分为5步,在test1()也就是线程1要执行的方法结尾将flag标识为true;那么线程二若是捕捉到主内存中的flag由false变为true,while循环体中内容应该会被执行,可是结果是while中内容并没有被执行。

    @Slf4j
    public class VolatileExample3 {
    	
    	volatile boolean flag=false;//定义初始化标识
    	public  void test1() {
    		log.info("-----线程1开始执行-----");
    		for (int i=0;i<5;i++) {
    			try {
    				Thread.sleep(1000);
    				log.info("test1初始化任务第"+i+"步已完成...");
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		flag = true;
    		log.info("初始化全部完成,flag变量已更改!");
    	}
    	
    	public  void test2() {
    		log.info("-----线程2开始执行-----");
    		while(flag) {
    			 log.info("test2()方法执行完毕!"); 
    			 break; 
    		}
    	}
    	
    	public static void main(String[] args) {
    		VolatileExample3 ve = new VolatileExample3();
    		ExecutorService executorService =Executors.newCachedThreadPool();
    		//线程1创建
    		executorService.execute(()->{
    			ve.test1();
    		});
    		//线程2创建
    		executorService.execute(()->{
    			ve.test2();
    		});
    	}
    }
    

    结果test2()的while中内容最终没有被执行:

    我怀疑线程二没有捕捉到flag在主内存中的变化,当我将test2()方法改成以下死循环,不停的给我打印flag值时:

    public  void test2() {
    		log.info("-----线程2开始执行-----");
    		while(true) {
    			log.info("flag:{}",flag);
    		}
    	}
    

    结果flag居然可以被捕捉到由false变为true,这说明线程一确实将自己工作内存的副本变量flag刷新到了主内存,并且线程2也更新了自己工作内存的值,而且我加了关键字volatile,当线程一更改了flag变量值,线程二应该马上就能看到修改后的值:

    说明flag确实是变化了的,那就奇了怪了,为什么直接写while(flag)却不能由条件不成立到条件成立呢?

    事实证明我是想多了,当我在while前后打出输出语句,如下:

    public  void test2() {
    		log.info("start");
    		while(flag) {
    			log.info("test2()方法执行完毕!");
    		}
    		log.info("end");
    	}
    

    结果end居然执行了,突然想起来首次运行条件flag为false,那一定不走循环体,直接输出end,线程整个过程都结束了还谈什么循环:

     INFO [pool-1-thread-2] - start
     INFO [pool-1-thread-1] - -----线程1开始执行-----
     INFO [pool-1-thread-2] - end
     INFO [pool-1-thread-1] - test1初始化任务第0步已完成...
     INFO [pool-1-thread-1] - test1初始化任务第1步已完成...
     INFO [pool-1-thread-1] - test1初始化任务第2步已完成...
     INFO [pool-1-thread-1] - test1初始化任务第3步已完成...
     INFO [pool-1-thread-1] - test1初始化任务第4步已完成...
     INFO [pool-1-thread-1] - 初始化全部完成,flag变量已更改!
    

    日常用法应该是这样:

    public  void test2() {		
    		  log.info("-----线程2开始执行-----"); 
    		  while(!flag) {
    		  log.info("test2()方法等待flag变化中~"); 
    		  } 
    		  log.info("test2()方法执行完毕!");
    	}
    

    结果:

     ......
     INFO [pool-1-thread-2] - test2()方法等待flag变化中~
     INFO [pool-1-thread-2] - test2()方法等待flag变化中~
     INFO [pool-1-thread-2] - test2()方法等待flag变化中~
     INFO [pool-1-thread-2] - test2()方法等待flag变化中~
     INFO [pool-1-thread-1] - test1初始化任务第4步已完成...
     INFO [pool-1-thread-1] - 初始化全部完成,flag变量已更改!
     INFO [pool-1-thread-2] - test2()方法等待flag变化中~
     INFO [pool-1-thread-2] - test2()方法执行完毕!
    

    看来基础还是需要巩固的啊。

  • 相关阅读:
    设计模式之访问者模式
    SecureCRT Win免安装版本,简单好用
    Java BAT大型公司面试必考技能视频-1.HashMap源码分析与实现
    最好用的百度云盘破解下载加速器
    博客系统学习关于应用架构的分层与设计
    博客系统实战——SprintBoot 集成Thymeleaf 实现用户增删查改(含源码)
    【设计模式】简单工厂模式
    快速开发平台全新升级,把时间留给自己,工作丢给力软平台
    快速开发平台全新升级,把时间留给自己,工作丢给力软平台
    The rapid development platform upgrade, leave the time to yourself, the work is lost to the soft platform
  • 原文地址:https://www.cnblogs.com/xusp/p/12696127.html
Copyright © 2011-2022 走看看