1.JUC线程高级-volatile关键字与内存可见性

简介: 1. 多线程操作共享变量引发问题我们通过下面代码来分析下内存可见性问题: package com.pyy.juc; public class TestVolatile { public static...

1. 多线程操作共享变量引发问题

我们通过下面代码来分析下内存可见性问题:

    package com.pyy.juc;
    
    public class TestVolatile {
    
        public static void main(String[] args) {
    
            // 这个线程为flag 修改值
            ThreadDemo td = new ThreadDemo();
            new Thread(td).start();
    
    
            // 主线程无线循环判断这个flag值
            while(true) {
                if(td.isFlag()) {
                    System.out.println("============");
                    break;
                }
            }
        }
    }
    
    class ThreadDemo implements Runnable {
        private boolean flag = false;
    
        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            flag = true;
    
            System.out.println("flag=" + isFlag());
        }
    
        public boolean isFlag() {
            return flag;
        }
    }

运行代码,会发现控制台输出:flag=true,但程序(主线程)并没有结束。这里就涉及到多线程的内存可见性问题:

img_102b9f201966ee25812f608b87a68ca9.png

出现上述问题的原因,主要是因为多个线程操作共享数据彼此不可见

解决内存可见性问题可以使用同步锁(synchronized)

        while(true) {
            synchronized(td) {
                if(td.isFlag()) {
                    System.out.println("============");
                    break;
                }
            }
        }

通过同步锁方式(可以保证每次都刷新主存,保证共享数据的同步性),但只要用到锁就会引发线程等待,影响代码效率。

2. volatile关键字

这里就引出了一个新的关键字volatile,它就可以保证多个线程操作共享数据内存中的数据是彼此可见的。底层是通过内存屏障,我们可以理解volatile修饰的变量是在主存中的数据直接操作。

读写变量都在 主存中直接操作,进而保证多线程可见性。而且效率上要比同步锁高的多。

img_15f4a90abe8fe6504f9697a474eca37d.png

其实有说volatile效率底,是因为jvm底层有个指令排序,但使用volatile修饰的变量会有一个禁止指令重排限制

3. 代码重构后

    ...
    // 使用volatile修饰变量
    private volatile boolean flag = false;
    ...

就可以解决多个线程操作共享数据彼此不可见问题

4. volatile和synchronized区别

volatile只是一个相比synchronized来说较为轻量级的同步策略

  1. volatile 不具备互斥性
  2. volatile 不能保证修饰变量的原子性

后面我们在谈论下有关变量原子性和CAS算法问题

目录
相关文章
|
3月前
|
存储 缓存 Java
【JavaEE】——内存可见性问题
volatile,一个线程读,一个线程写,两个线程互相读,多个线程多把锁
|
4月前
|
监控 Java 数据库连接
线程池在高并发下如何防止内存泄漏?
线程池在高并发下如何防止内存泄漏?
181 6
|
5月前
|
监控 Java 数据库连接
使用线程池时,如何避免内存泄漏的问题?
使用线程池时,如何避免内存泄漏的问题?
|
5月前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
90 2
|
5月前
|
监控 数据可视化 Java
如何使用JDK自带的监控工具JConsole来监控线程池的内存使用情况?
如何使用JDK自带的监控工具JConsole来监控线程池的内存使用情况?
|
5月前
|
缓存 Java 编译器
【多线程-从零开始-伍】volatile关键字和内存可见性问题
【多线程-从零开始-伍】volatile关键字和内存可见性问题
83 0
|
1月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
48 17
|
1月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
59 26
|
3月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
279 2
|
4月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####