四、并发编程三大特性之一:可见性

可见性

在电脑硬件中,CPU缓存要比内存的读取效率高很多,所以CPU会将内存中的数据读取到缓存中后进行操作。

每个线程都有自己的缓存,一个线程将内存中的数据读取到缓存中进行修改后,其他线程是感知不到的,这就出现了可见性问题

可见性问题代码逻辑如下:

java 复制代码
public class test {

    private static boolean flag = true;

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            while (flag) {

            }
            System.out.println("t1线程结束");
        });

        t1.start();
        Thread.sleep(1000);
        flag = false;
        System.out.println("main线程结束");
    }
}

保证可见性

volatile关键字

volatile关键字修饰在成员变量上,线程对共享变量进行修改后,其他线程可以感知到

volatile属性被写:当修改一个volatile变量,JMM会将当前线程对应的缓存中的数据及时刷新到内存中

volatile属性被读:当读取一个volatile变量,JMM会将缓存中对应的数据设置为无效,必须从内存中读取

volatile修饰的数据操作编译成汇编指令后,会追加lock前缀,cpu执行时,会做两件事:

  • 将当前缓存行的数据写回主内存
  • 其他CPU缓存中存储的此数据无效
java 复制代码
public class test {

    private volatile static boolean flag = true;

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            while (flag) {

            }
            System.out.println("t1线程结束");
        });

        t1.start();
        Thread.sleep(1000);
        flag = false;
        System.out.println("main线程结束");
    }
}

synchronized关键字

synchronized保证可见性的机制:

synchronized修饰的同步代码块或者同步方法,获取锁资源后,会将代码块中涉及的变量从缓存中移除,必须从内存中重新读取。并且释放锁资源后,会立即将缓存中的数据写到内存中

java 复制代码
public class test {

    private static boolean flag = true;

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            while (flag) {
                synchronized (test.class) {
                }
            }
        });

        t1.start();
        Thread.sleep(1000);
        flag = false;
        System.out.println("main线程结束");
    }
}

lock锁

lock锁是基于volatile实现的。lock内部在加锁和释放锁时,会对volatile修饰的state变量进行加减操作

如果对volatile修饰的变量进行修改操作,cpu会执行lock前缀的指令。cpu在修改数据后,会立即同步到内存中,并且将其他变量值也同步到内存中。并且会将其他cpu缓存中的数据设置成无效,必须从内存中重新获取

lock与synchronized相同,也是加锁时将代码块中涉及的变量从缓存中移除,再从内存中读取

java 复制代码
public class test {

    private static boolean flag = true;

    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            while (flag) {
                lock.lock();
            }
            try {

            } finally {
                lock.unlock();
            }
            System.out.println("t1线程结束");
        });

        t1.start();
        Thread.sleep(1000);
        flag = false;
        System.out.println("main线程结束");
    }
}
相关推荐
我是人✓2 分钟前
Spring框架的事务管理
java·后端·spring
javachen__15 分钟前
JsonView:一套VO实现多角色字段返回
java·linux·服务器
行者-全栈开发32 分钟前
SpringBoot 生产环境踩坑实录:10个真实故障案例与教训
java·spring boot·后端
李福春35 分钟前
斗破Google ADK Java苍穹-第7境斗皇-御天地-Harness治理
java·人工智能·python·深圳同盟·腾讯云架构师技术同盟·adk java·harness 治理
Wang's Blog36 分钟前
Java 项目部署之 Docker工具快速入门: 容器命令练习:创建、改文件、重启到删除的完整闭环
java·开发语言·docker
我是人✓37 分钟前
Java面向对象核心特性深度解析
java·前端·算法
Wx-bishekaifayuan41 分钟前
springboot线上中医药养生平台管理系统36753-计算机课程设计、毕业设计
java·spring boot·后端·spring·课程设计·express·旅游
Wx-bishekaifayuan1 小时前
springboot大学生就业平台47691-计算机课程设计、毕业设计
java·spring boot·后端·elasticsearch·课程设计·express·旅游
汤姆yu1 小时前
基于springboot+vue的企业OA办公系统
java·spring boot·后端·oa办公
南归北隐1 小时前
Spring AI Alibaba Graph框架实现RAG私有知识库问答
java·后端·spring·spring ai·java rag实现