java多线程(六)关键字Volatile可见性、有序性以及单个变量的原子性

volatile关键字

作用

volatile 是 Java 虚拟机提供的轻量级的同步机制,主要用来确保变量被线程安全地读取和写入。

当一个变量定义为 volatile 后,它具备以下特性:

  1. 可见性:确保不同线程对这个变量操作的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

  2. 有序性:禁止进行指令重排序优化。

实现原理

在 Java 内存模型中,每个线程在运行时都有自己的工作内存,其中存放着主内存中变量副本的拷贝。

当线程对变量进行修改时,首先修改的是自己工作内存中的副本,然后线程再将修改后的副本值刷新到主内存中。

当其他线程需要读取该变量时,会从主内存中重新读取最新的副本到自己的工作内存中。

volatile 变量的特殊规则如下:

  • 每次使用前都需要从主内存中刷新最新的值,保证读取的总是最新的数据。

  • 每次修改后都需要立即同步回主内存中,保证其他线程可以看到最新的值。

使用场景
  1. 状态标记量:如中断标记、状态标记等。

  2. 单例模式的双重检查锁定:防止指令重排序。

注意事项
  1. volatile 变量不适合作为状态的复合操作,因为 volatile 不能保证复合操作的原子性。

  2. volatile 只能保证变量的可见性和有序性,不能保证原子性。

volatile示例讲解

1. 可见性

Volatile保证了变量的可见性,即当一个线程修改了某个变量的值,这个新值对其他线程来说是立即可见的。

反面代码示例:不使用volatile时,可能导致线程读取到过时数据。

package com.hmblogs.backend.study.thread;

public class VolatileExample {
    // 假设threadA和threadB同时访问isFlag变量,且没有使用volatile关键字
    static boolean isFlag = false;
    public static void main(String[] args) throws InterruptedException {


        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            // thread1中执行
            isFlag = true;
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            // thread2中执行
            if (!isFlag) {
                // 这里的代码可能会执行,即使isFlag已经被thread1设置为true
                System.out.println("isFlag 是 false.");
            }
        });

        thread1.start();
        thread2.start();

        System.out.println("Both threads have finished their execution");
    }
}

正面代码示例:使用volatile确保变量值的实时更新。

package com.hmblogs.backend.study.thread;

public class VolatileExample {
    // 假设threadA和threadB同时访问isFlag变量,且没有使用volatile关键字
    volatile static boolean isFlag = false;
    public static void main(String[] args) throws InterruptedException {


        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            // thread1中执行
            isFlag = true;
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            // thread2中执行
            if (!isFlag) {
                // 这里的代码可能会执行,即使isFlag已经被thread1设置为true
                System.out.println("isFlag 是 false.");
            }
        });

        thread1.start();
        thread2.start();

        System.out.println("Both threads have finished their execution");
    }
}

验证结果:volatile没有对应的特性。这是什么原因呢?

2. 原子性

Volatile对单个变量的读写具有原子性,但复合操作(如i++)不是原子的。

反面代码示例:volatile变量在复合操作中可能出现问题。

package com.hmblogs.backend.study.thread;

public class VolatileExample {
    static volatile int counter = 0;
    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            // thread1中执行
            counter++;
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            // thread2中执行
            System.out.println("counter 是 "+counter+".");
        });

        thread1.start();
        thread2.start();

        System.out.println("Both threads have finished their execution");
    }
}

有时候打印的是1,有时候是0

正面代码示例:使用synchronized或原子类来保证复合操作的原子性。

package com.hmblogs.backend.study.thread;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileExample {
    static volatile AtomicInteger counter = new AtomicInteger();
    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            // thread1中执行
            int temp = counter.getAndAdd(1);
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            // thread2中执行
            System.out.println("counter 是 "+counter.get()+".");
        });

        thread1.start();
        thread2.start();

        System.out.println("Both threads have finished their execution");
    }
}

一直都是0.

验证结果:volatile没有对应的特性。这是什么原因呢?

3. 有序性

Volatile变量还可以确保指令的有序性,防止JVM进行指令重排序。

反面代码示例:不使用volatile时,JVM可能进行指令重排序。

int a = 0;
boolean flag = false; // 假设这里不使用volatile
int b = 0;

// 线程A执行
a = 1; // Step 1
flag = true; // Step 2
b = 2; // Step 3

// 线程B执行
if (flag) {
    // JVM可能重排序,导致线程B先看到b=2,再看到a=1
}

 正面代码示例:使用volatile防止指令重排序。

int a = 0;
volatile boolean flag = false;
int b = 0;

// 线程A执行
a = 1; // Step 1
flag = true; // Step 2,volatile变量,确保前面操作不会重排序到后面
b = 2; // Step 3

// 线程B执行
if (flag) {
    // 确保先看到a=1,再看到b=2
}

最后

Volatile是Java多线程编程中的一个重要关键字,它确保了变量的可见性、有序性和部分原子性。

正确使用volatile关键字可以避免多线程中的一些问题,如数据不一致和指令重排序等。

然而,需要注意的是,volatile并不能保证复合操作的原子性,对于这类操作,需要使用synchronized或原子类来保证线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值