并发编程三特性-可见性技术保障

场景说明:

  • 存在两个线程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变量
原子性 无法保障 可以保障 可以保障 可以保障
可见性 可以保障 可以保障 可以保障 可以保障
有序性 一定程度保障 可以保障 可以保障 无法保障

猜你喜欢

转载自blog.csdn.net/yz18931904/article/details/81349691