JUC多线程与高并发(一)

1.谈谈对volatile的理解

  • volatile是java虚拟机提供的一种轻量级的同步机制

    • 保证可见性
    • 不保证原子性
    • 禁止指令重排
  • 相当于轻量级的sychronized

1.1. JMM的理解
  • JMM,java内存模型

    Java Memory Model,是一种 抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。

  • JMM关于同步的规定:

    • 线程解锁前,必须把共享变量的值刷新回主内存;
    • 线程加锁前,必须读取主内存的最新值到自己的工作内存(或叫栈空间);
    • 加锁解锁是同一把锁
  • 过程相关

    • 由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存;
    • 工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可访问;
    • 但是线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存;不能直接操作主内存中的变量,各个线程中的工作内存存储着主内存中的变量拷贝副本;
    • 因此,不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。
1.2 volatile的使用场景

volatile关键字在计算机编程中主要用于表示一个变量可能会被意想不到地改变,例如由操作系统的其他部分、硬件或其他线程。它告诉编译器不要对该变量的访问进行优化,以确保每次访问时都直接从内存读取该变量的值,而不是使用寄存器中的缓存值。

volatile在以下场景中经常被使用:

  1. 中断服务程序:当中断服务程序中修改的变量需要被其他程序检测时,通常需要将该变量声明为volatile。这确保了在中断发生时,变量的更新能够立即被其他程序所感知。
  2. 多任务环境:在多任务环境中,当多个任务需要共享某个标志时,该标志应该被声明为volatile。这确保了当一个任务修改了这个标志后,其他任务能够立即看到这个变化。
  3. 硬件寄存器:对于存储器映射的硬件寄存器,通常也需要使用volatile进行声明。这是因为每次对硬件寄存器的读写都可能具有不同的意义,使用volatile可以确保每次读写都直接从硬件获取或写入数据。
  4. 多线程共享变量:在多线程编程中,如果一个属性(如循环标识)被多个线程共享,并且一个线程修改了此属性,那么可以使用volatile确保其他线程能够立即获得修改后的值。
  5. 单例模式volatile还可以用于解决单例模式中的双重检查锁定(double-checked locking)问题。在多线程环境下,可能会出现指令重排序的情况,导致空指针问题。通过使用volatile,可以确保对象的初始化在多线程环境下是线程安全的。

请注意,虽然volatile可以提供一定程度的线程安全,但它并不能解决所有并发问题。对于更复杂的并发场景,如确保操作的原子性或解决数据竞争问题,可能还需要使用其他同步机制,如锁或原子变量。

在Java编程中,volatile关键字经常用于确保多线程环境下的变量可见性和有序性。以下是一些Java代码中使用volatile的示例:

示例1:停止线程

当我们需要停止一个线程时,可以设置一个volatile的布尔标志,这样其他线程就可以检查这个标志来决定是否停止执行。

public class StopThreadExample {  
    private volatile boolean stop = false;  
  
    public void stopThread() {  
        stop = true;  
    }  
  
    public void run() {  
        while (!stop) {  
            // 执行任务  
        }  
    }  
}

在这个例子中,stop变量被声明为volatile,以确保当stopThread方法被调用并设置stoptrue时,运行中的线程能够立即看到这个变化,并退出循环。

示例2:单例模式的双重检查锁定

volatile关键字也常用于实现线程安全的单例模式。

public class Singleton {  
    // 使用volatile确保instance在多线程环境下的可见性  
    private static volatile Singleton instance;  
  
    private Singleton() {}  
  
    public static Singleton getInstance() {  
        if (instance == null) {  
            synchronized (Singleton.class) {  
                if (instance == null) {  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}

在这个例子中,instance变量被声明为volatile,以确保在多线程环境下,当instance被初始化后,其他线程能够立即看到它。这避免了在多线程环境下出现null引用的风险。

示例3:状态标志

在多线程环境中,有时我们需要共享一个状态标志,volatile可以确保状态的实时更新能够被其他线程看到。

public class SharedState {  
    // 使用volatile确保state的实时更新对其他线程可见  
    private volatile boolean state = false;  
  
    public void setState(boolean newState) {  
        state = newState;  
    }  
  
    public boolean getState() {  
        return state;  
    }  
}

在这个例子中,state变量是一个状态标志,被声明为volatile以确保当一个线程修改它的值时,其他线程能够立即看到这个变化。

注意事项

虽然volatile在某些情况下非常有用,但它并不提供原子性保证。对于需要原子性操作的场景(例如,递增计数器),应该使用AtomicInteger等原子类,或者使用synchronized关键字来确保操作的原子性。此外,对于更复杂的并发控制需求,如防止数据竞争和确保操作的顺序性,通常还需要结合使用其他同步机制。

你可能感兴趣的:(计算机类,linux,java,jvm,缓存)