JAVA并发编程补充-线程可见性
Posted Gggoblin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA并发编程补充-线程可见性相关的知识,希望对你有一定的参考价值。
定义一个类Visibility1包含两个变量
public class Visibility1 {
public static boolean ready;
public static int number;
}
自定义一个线程类,当Visibility1中的ready属性为false时进入循环打印number
public class ReaderThread extends Thread{
@Override
public void run() {
while (!Visibility1.ready){
Thread.yield();
System.out.println(Visibility1.number);
}
}
}
测试线程
public class TestThread1 {
public static void main(String[] args) {
new ReaderThread().start();
Visibility1.number = 42;
Visibility1.ready = true;
}
}
输出结果有三种:
0:这种情况是先进入了循环执行了一次输出;
42:这种情况是number被赋值42之后进入的循环;
空:这种情况是ready直接被赋值为true,没有进入循环;
这里涉及到一个指令排序的问题,例如:int a = 1; int b = 2; int c = a+b;这三条语句cpu有可能先执行第二条,但不会先执行第三条;(cpu的优化手段,有可能的第二条执行的时间比第一条短)。
这里写了一个测试类Visibility其中有一个bChanged变量,两个无限循环的线程,具体看注解,理论上说这个程序是会停止的,但是执行结果确实没有停止
public class Visibility {
private static boolean bChanged;
public static void main(String[] args) throws InterruptedException {
/**
* 第一个线程,当bChanged为true的时候输出 "!=" 然后退出java虚拟机
*/
new Thread(()-> {
for ( ; ; ) {
if (bChanged == true){
System.out.println("!=");
System.exit(0);
}
}
}).start();
/**
* 第二个线程,将本次、bChanged赋值为true
*/
Thread.sleep(10);
new Thread(()-> {
for (;;){
bChanged = true;
}
}).start();
}
}
原因如下图,由于cpu运行太快在cpu内存之间还有一层缓存,进入死循环没有退出是因为第一个线程的变量是读取的缓存。
解决方法:使用 volatile关键字:1保证变量的修改让所有线程可见;2阻止指令排序;
volatile是比较古老的关键字 synchroized已经优化的很好了,不要去刻意的使用volatile
private static volatile boolean bChanged;
sync能够解决可见性、原子性 volatile只能解决 如果CAS操作没有原子化多线程操作一定会有问题 先比较在修改CAS操作 compare and set
线程并发问题:写或者读到了一个过期的数据,解决方式
1.线程封闭:final 不要线程之间共享变量 (约等于没用过)
2.栈封闭:比如说方法内部声明,修改 不会溢出,方法执行完就释放,不存在于内存
3.ThreadLocal线程绑定。
以上是关于JAVA并发编程补充-线程可见性的主要内容,如果未能解决你的问题,请参考以下文章
[Java并发编程之美]第2章 并发编程的其他基础知识 补充知识
JAVA并发编程递进篇,探索线程安全性volatile关键字如何保证可见性
JAVA并发编程递进篇,探索线程安全性volatile关键字如何保证可见性
Java 并发编程 -- 并发编程线程基础(线程安全问题可见性问题synchronized / volatile 关键字CASUnsafe指令重排序伪共享Java锁的概述)