您的位置:首页 > 财经 > 金融 > 企业代运营公司_直播系统开发公司_市场推广和销售的区别_seo链接优化

企业代运营公司_直播系统开发公司_市场推广和销售的区别_seo链接优化

2025/1/7 23:45:39 来源:https://blog.csdn.net/weixin_35691921/article/details/143427490  浏览:    关键词:企业代运营公司_直播系统开发公司_市场推广和销售的区别_seo链接优化
企业代运营公司_直播系统开发公司_市场推广和销售的区别_seo链接优化

Volatile

    • 1. 概念介绍
    • 2. 可见性问题背景
    • 3. `@Volatile`的工作原理
    • 4. 与其他同步机制的比较
    • 5. 防止指令重排序(在一定程度上)
  • 参考地址

1. 概念介绍

  • @Volatile是Java中的一个关键字,用于修饰变量。它主要用于解决多线程环境下变量的可见性问题。当一个变量被声明为volatile时,这意味着该变量的值在多个线程之间是“可见”的。

2. 可见性问题背景

  • 在多线程环境中,如果没有适当的同步机制,一个线程对共享变量的修改可能不会及时被其他线程看到。这是因为每个线程可能会在自己的工作内存(缓存)中保留变量的副本。例如,线程A修改了一个共享变量的值,但线程B可能仍然在使用它自己缓存中的旧值,直到某个不确定的时间点才会更新这个值。

3. @Volatile的工作原理

  • 当一个变量被标记为volatile时,每次读取这个变量时,线程会直接从主内存中读取,而不是从自己的工作内存中读取。同样,每次修改这个变量时,线程会立即将新值刷新到主内存中。这样就保证了其他线程能够及时看到变量的最新值。
  • 例如,假设有一个共享的volatile变量count
    public class VolatileExample {@Volatileprivate static int count = 0;public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {count++;}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {count++;}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: " + count);}
    }
    
  • 在这个例子中,count变量被声明为volatile。两个线程thread1thread2都对count进行自增操作。由于countvolatile的,每个线程在修改count后会立即将新值刷新到主内存,并且在读取count时会从主内存读取最新的值。不过需要注意的是,volatile并不能保证原子性,在这个例子中,count++操作实际上包含了读取、修改和写入三个步骤,可能会出现并发问题(最终的count值可能小于2000),但它确实保证了可见性。

4. 与其他同步机制的比较

  • synchronized的比较
    • synchronized关键字不仅保证了变量的可见性,还保证了操作的原子性。它通过对代码块或者方法进行加锁,使得同一时间只有一个线程能够访问被锁定的部分。而volatile主要关注的是变量的可见性,对于复杂的复合操作(如count++),它不能保证原子性。
    • 例如,使用synchronized来保证count++操作的原子性可以这样做:
      public class SynchronizedExample {private static int count = 0;public static void main(String[] args) {Thread thread1 = new Thread(() -> {synchronized (SynchronizedExample.class) {for (int i = 0; i < 1000; i++) {count++;}}});Thread thread2 = new Thread(() -> {synchronized (SynchronizedExample.class) {for (int i = 0; i < 1000; i++) {count++;}}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: " + count);}
      }
      
  • java.util.concurrent.atomic包的比较
    • java.util.concurrent.atomic包提供了一系列原子类,如AtomicIntegerAtomicLong等。这些原子类在保证变量可见性的同时,还能保证操作的原子性。例如,AtomicIntegerincrementAndGet操作是原子的。
    public class AtomicExample {private static AtomicInteger count = new AtomicInteger(0);public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {count.incrementAndGet();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {count.incrementAndGet();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: " + count.get());}
    }
    
    • volatile相比,原子类提供了更高级的并发控制功能,适用于需要原子操作的场景。而volatile更像是一种轻量级的可见性保证机制,用于简单的共享变量场景。

5. 防止指令重排序(在一定程度上)

  • 指令重排序介绍:在现代处理器和编译器优化中,为了提高程序执行效率,可能会对指令进行重新排序。在单线程环境下,这种重排序不会影响程序的正确性。但在多线程环境下,可能会导致问题。volatile关键字可以在一定程度上防止指令重排序。
  • 示例场景:考虑一个简单的单例模式实现(双重检查锁定模式),在这个模式中需要防止指令重排序。
    public class Singleton {private volatile static 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时(instance = new Singleton();),这个操作实际上包含了三个步骤:分配内存空间、初始化对象、将对象引用赋值给instance变量。如果没有volatile,处理器或编译器可能会对这三个步骤进行重排序。例如,先将对象引用赋值给instance变量,然后再初始化对象。在其他线程访问instance时,可能会获取到一个还没有完全初始化的对象,导致程序出现错误。使用volatile可以防止这种指令重排序,保证了对象在被引用之前已经完成初始化。

参考地址

AI 豆包

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com