问题描述:
-
子线程死循环里加休眠,会跳出死循环,执行while后面的语句?
-
子线程死循环里加打印,会跳出死循环,执行while后面的语句?
问题代码:
package com.demo;
import java.util.concurrent.TimeUnit;
/**
* @Description
* @Author by mocar小师兄
* @Date 2020/3/13 16:54
**/
public class Voletile_Thread {
private static boolean Flag=false;
private static int count=0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始执行任务");
while (!Flag) {
//子线程死循环里加休眠,会跳出死循环,执行while后面的语句?
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
//子线程死循环里加打印,会跳出死循环,执行while后面的语句?
System.out.println("count:\t" + count);
}
System.out.println( Thread.currentThread().getName() +"执行结束");
}
});
thread1.start();
System.out.println("Main主线程等待1秒");
Thread.sleep(200);
Flag=true;
System.out.println("Main主线程修改了Flag值,主线程结束");
}
}
当注释掉休眠和打印的语句,子线程可以正常死循环!
package com.demo;
import java.util.concurrent.TimeUnit;
/**
* @Description
* @Author by mocar小师兄
* @Date 2020/3/13 16:54
**/
public class Voletile_Thread {
private static boolean Flag=false;
private static int count=0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始执行任务");
while (!Flag) {
//子线程死循环里加休眠,会跳出死循环,执行while后面的语句?
/*try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
count++;
//子线程死循环里加打印,会跳出死循环,执行while后面的语句?
//System.out.println("count:\t" + count);
}
System.out.println( Thread.currentThread().getName() +"执行结束");
}
});
thread1.start();
System.out.println("Main主线程等待1秒");
Thread.sleep(200);
Flag=true;
System.out.println("Main主线程修改了Flag值,主线程结束");
}
}
解释:
为什么子线程死循环里加打印/休眠,会跳出死循环?
1、先说下多线程下的内存模型
对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。
2、
先解释正常情况下,子线程中不加 sleep 和 System.out.println("count:\t" + count),如下:
一开始主线程中的Flag的变量值为false,创建了一个子线程Thread-0,将Flag复制到运行内存中,因为Thread-0在运行的时候Flag一直都是false,因此while循环会一直运行。虽然后面主线程休眠了一小会后,改变了主内存里面Flag为true了,但是影响不了Thread-0运行内存中的Flag的值。因此Thread-0会一直在while中无限循环。
现在加了println后,因为println操作是Synchronized加锁的,它会做以下的操作:
- 1、获得同步锁;
- 2、清空工作流出来;
- 3、从主内存拷贝对象副本到线程工作内存中;
- 4、开始继续执行代码
- 5、刷新主内存数据
- 6、释放同步锁。
System.out.println源码如下:
在清空内存刷新内存的过程中,Thread-0线程有这么一个操作:获取锁到释放锁。Thread-0线程的Flag就变成了true(从主内存拷贝对象副本到线程工作内存中),所以就跳出了循环。指令重排序的情况也就不会出现了,这也是Volatile关键字的两种特性之一。
子线程中加休眠也是一个道理,调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放。当锁释放后,又会从从主内存拷贝对象副本到线程工作内存中。