生活
生活就是生下来,活下去。
————
在JAVA并发编程,如果要保证程序的线程安全,就要保证代码的原子性、可见性、有序性。
昨天聊了原子性。今天来看下可见性、有序性。
什么是可见性?
当多个线程访问同一个变量时,一个线程修改了一个变量的值,其他线程可以马上读取到修改后的值。
可见性问题
场景说明:
两个线程共享一个变量stop.
A线程,当stop为false,则一直运行;为true则停止。
B线程,设置stop为true
public class Test {
static boolean stop;
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程A运行中");
while (!stop);
System.out.println("线程A已停止");
}
}).start();
Thread.sleep(10);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程B运行中");
stop = true;
System.out.println("stop设置为true,线程B已停止");
}
}).start();
}
}
其中一次运行结果:
线程A运行中
线程B运行中
stop设置为true,线程B已停止
由此见得,一个普通的变量在多线程环境下不具有可见性。
可见性的技术保障
1、Lock
2、synchronized
3、原子类
4、volatile
Lock和synchronized不用说,都是互斥锁,保障了共享资源在同一时刻只有一个线程可以访问,就不会出现可见性的问题。
volatile保障了一个变量修改以后马上写回主内存让其他线程可以看到,具体的分析后面再说。
至于原子类为什么也有可见性,因为他里面的value本身也是volatile修饰的。
关于volatile的详细解析,后面开一篇细聊。
什么是有序性?
即程序执行的顺序按照代码的先后顺序执行。
编译器和处理器为了提高执行的效率性能,会对执行进行重排序,指令重排序对单线程应用没有影响,但是在多线程环境下可能会出现问题。
有序性问题
线程A
context = loadContext();
inited = true;
线程B
while(!inited ){
sleep
}
doSomethingwithconfig(context);
假设线程A发生重排序:
inited = true;
context = loadContext();
在这种情况下,B线程可能会拿到没有初始化的变量context。
有序性的技术保障
1、synchronized
2、lock
3、volatile(一定程度的有序性)
synchronized和lock在同一时刻只能让一个线程执行同步方法,当然就是有序的了。
volatile的有序性是指写入修改后的volatile变量这个操作必定优先于 读取这个变量。