并发编程三特性-可见性技术保障
场景说明:
- 存在两个线程A、线程B和一个共享变量stop。
- 如果stop变量的值是false,则线程A会一直运行。如果stop变量的值是true,则线程A会停止运行。
- 线程B能够将共享变量stop的值修改为ture。
定义普通的共享变量:
//普通情况下,多线程不能保证可见性 private static boolean stop;
下面的代码展示了在多线程环境中,对共享变量的共享:
//普通情况下,多线程不能保证可见性
new Thread(() -> { System.out.println("Ordinary A is running...");
while (!stop) ;
System.out.println("Ordinary A is terminated."); }).start();
Thread.sleep(10);
new Thread(() -> {
System.out.println("Ordinary B is running...");
stop = true;
System.out.println("Ordinary B is terminated.");
}).start();
某次运行结果:
Ordinary A is running...
Ordinary B is running...
Ordinary B is terminated.
通过观察结果,发现程序确实存在可见性问题。
3.可见性技术保障
在Java中提供了多种可见性保障措施,这里主要涉及四种:
- 通过volatile关键字标记内存屏障保证可见性。
- 通过synchronized关键字定义同步代码块或者同步方法保障可见性。
- 通过Lock接口保障可见性。
- 通过Atomic类型保障可见性。
3.1.volatile关键字
使用volatile关键字修饰共享变量stop:
//使用volatile能够保证可见性 private volatile static boolean vStop;
- 在多线程环境中,对volatile修饰的共享变量stop进行共享测试:
- //通过volatile关键字保证可见性
- new Thread(() -> { System.out.println("Volatile A is running...");
- while (!vStop) ; System.out.println("Volatile A is terminated."); }).start();
- new Thread(() -> {
- System.out.println("Volatile B is running...");
- vStop = true;
- System.out.println("Volatile B is terminated.");
- }).start();
运行结果(多次):
Volatile A is running...
Volatile B is running...
Volatile B is terminated. Volatile A is terminated.
通过多次运行,发现运行结果一致,所以可以确定volatile关键字能够保证代码的可见性。
3.2.synchronized关键字
定义一个共享对象用于synchronized关键字进行同步加锁:
//通过synchronized同步代码块保证可见性
private static byte[] obj = new byte[0];
在多线程环境中进行对obj进行加锁、等待和唤醒:
运行结果(多次):
Synchronized A is running...
Synchronized B is running...
Synchronized B is terminated.
Synchronized A is terminated.
通过多次运行,发现运行结果一致,所以可以确定synchronized关键字能够保证代码的可见性。
3.3.Lock接口
定义Lock接口和Condition接口:
//通过Lock接口保证可见性
private static ReentrantLock lock = new ReentrantLock(true);
private static Condition condition = lock.newCondition();
在多线程环境中进行Lock加锁、condition等待和唤醒:
- 运行结果(多次):
- Lock A is running...
- Lock B is running...
- Lock B is terminated.
- Lock A is terminated.
通过多次运行,发现运行结果一致,所以可以确定Lock接口能够保证代码的可见性。
3.4.Atomic类型
使用AtomicBoolean定义共享变量stop:
//通过Atomic保证可见性
private static AtomicBoolean aStop = new AtomicBoolean(false);
在多线程环境中,对使用AtomicBoolean定义的共享变量stop进行共享测试:
//通过Atomic保证可见性
new Thread(() -> {
System.out.println("Atomic A is running...");
while (!aStop.get()) ;
System.out.println("Atomic A is terminated.");
}).start();
new Thread(() -> {
System.out.println("Atomic B is running...");
aStop.set(true);
System.out.println("Atomic B is terminated.");
}).start();
运行结果(多次):
Atomic A is running...
Atomic B is running...
Atomic B is terminated.
Atomic A is terminated.
通过多次运行,发现运行结果一致,所以可以确定Atomic类型能够保证代码的可见性。
4.总结
经验证,以下四种措施,可以保证Java代码在运行时的可见性:
- volatile关键字
- synchronized关键字
- Lock接口
- Atomic类型
并发三特性总结
特性 | volatile关键字 | synchronized关键字 | Lock接口 | Atomic变量 |
---|---|---|---|---|
原子性 | 无法保障 | 可以保障 | 可以保障 | 可以保障 |
可见性 | 可以保障 | 可以保障 | 可以保障 | 可以保障 |
有序性 | 一定程度保障 | 可以保障 | 可以保障 | 无法保障 |