首页 > 其他分享 >详解线程的三大特性:原子性、可见性和有序性

详解线程的三大特性:原子性、可见性和有序性

时间:2024-11-21 09:30:34浏览次数:1  
标签:synchronized 原子 flag volatile 有序性 线程 三大

在多线程编程中,理解线程的 原子性可见性有序性 是构建正确并发程序的基础。以下是它们的详细解释:


1. 原子性 (Atomicity)

定义 原子性指的是操作不可被中断,要么全部执行完成,要么完全不执行。

特性

  • 原子性操作在执行时不会被其他线程干扰。

  • 如果多个线程同时访问共享资源,原子性可以防止数据的不一致。

Java 中的原子性

  • 原子性操作示例:

    • 读取和写入基本数据类型(如 intfloat)是原子性的。

    • volatile 变量的读取/写入是原子性的(不适用于复合操作)。

  • 非原子性操作:

    • 复合操作(如 counter++counter = counter + 1)是非原子性的。这些操作实际上包括三步:读取变量值、修改值、写回变量。

解决方案

  • 使用 同步机制(如 synchronized 块或方法):

    synchronized (lock) {
        counter++;
    }
  • 使用 原子类(如 AtomicInteger):

    AtomicInteger counter = new AtomicInteger();
    counter.incrementAndGet();
    

      


2. 可见性 (Visibility)

定义 可见性指的是一个线程对共享变量的修改对其他线程是可见的。

特性

  • 在多线程环境中,如果没有同步机制,一个线程对变量的修改可能不会立刻被其他线程看到(由于 CPU 缓存或编译优化)。

  • 线程可能会一直使用自己 CPU 缓存中的值,而看不到其他线程更新后的值。

Java 中的可见性

  • 存在可见性问题的场景:

    private static boolean flag = false;
    ​
    public static void main(String[] args) {
        new Thread(() -> {
            while (!flag) {
                // do something
            }
            System.out.println("Thread ended.");
        }).start();
    ​
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    ​
        flag = true; // 主线程修改了 flag,但子线程可能看不到
    }
  • 解决方案:

    1. 使用 volatile 关键字:

      private static volatile boolean flag = false;
      • volatile 确保变量的修改对所有线程立即可见。

    2. 使用 同步机制(如 synchronized 或显式锁 Lock),因为同步也能保证可见性。


3. 有序性 (Ordering)

定义 有序性指的是程序代码的执行顺序,通常来说,程序会按照代码编写的顺序执行,但编译器和处理器会为了优化性能进行 指令重排

特性

  • 在单线程环境中,指令重排不会影响程序的正确性。

  • 在多线程环境中,指令重排可能导致意想不到的结果,因为线程之间的执行顺序无法预测。

指令重排的例子

int a = 1; // (1)
int b = 2; // (2)
int c = a + b; // (3)

在单线程中,执行顺序是 (1) -> (2) -> (3),但由于重排优化,CPU 可能将 (2)(1) 的顺序交换。尽管结果在单线程环境中是正确的,但多线程中可能导致数据问题。

Java 中的有序性

  • 可能出现问题的场景:

    • 使用 volatile 可以禁止指令重排:

      package com.example.demopool;
      
      import org.springframework.stereotype.Component;
      
      /**
       * @Author: cv master
       * @Date: 2024/11/15 09:17
       */
      
      @Component
      public class B {
          private boolean flag = false;
          private int a = 0;
      
          public void writer() {
              a = 1;       // 写变量
              flag = true; // 通知其他线程
          }
      
          public void reader() {
              if (flag) { // 读标志
                  System.out.println(a); // 此时 a 的值一定是 1
              }
          }
      
          public static void main(String[] args) throws InterruptedException {
              B b = new B();
              Thread thread1 = new Thread(b::writer);
              Thread thread2 = new Thread(b::reader);
              thread1.start();
              thread2.start();
              thread1.join();
              thread2.join();
          }
      }
    • 在上述代码中,volatile 保证了写操作的顺序,使得 a = 1 一定发生在 flag = true 之前。

解决方案

  • 使用 volatile:确保关键变量的修改不会被指令重排。

  • 使用 synchronized显式锁:同步代码块可以强制线程按照指定顺序执行。


三者关系与 happens-before 原则

  • 原子性可见性 是独立的,但有时需要结合使用才能实现正确的多线程行为。

  • 有序性 通常需要通过 volatile 或同步机制来确保。

  • happens-before 是 Java 内存模型中定义的一种原则,用于规定线程间的操作顺序:

    • 一个线程对变量的写操作对另一个线程的读操作可见,必须满足 happens-before 原则。

    • 如:synchronizedvolatile、线程启动/终止等操作会建立 happens-before 关系。


总结

特性描述解决方法
原子性 操作不可中断,要么全部执行成功,要么完全不执行。 使用 synchronized 或原子类如 AtomicInteger
可见性 一个线程的修改对其他线程立刻可见。 使用 volatile 或同步机制如 synchronized
有序性 程序执行顺序符合预期,避免指令重排导致问题。 使用 volatile、同步机制(synchronized 或锁)。

标签:synchronized,原子,flag,volatile,有序性,线程,三大
From: https://www.cnblogs.com/luorongxin/p/18559925

相关文章

  • Java线程池创建
    ......
  • OpenCV三大经典项目实战:车辆检测、人脸识别、图像拼接与文字识别
    OpenCV三大经典项目实战:车辆检测、人脸识别、图像拼接与文字识别OpenCV(OpenSourceComputerVisionLibrary)作为计算机视觉领域的核心库之一,提供了丰富的功能和高效的算法,广泛应用于车辆检测、人脸识别、图像拼接和文字识别等多个领域。本文将详细介绍如何使用OpenCV进行这三大......
  • Qt - 多线程之并发(QtConcurrent)
    一、什么是QtConcurrent?Concurrent是并发的意思,而QtConcurrent同std一样,是一个命名空间(namespace)。提供了一些高级的API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。对于QtC......
  • Qt - 多线程之线程同步
    一、线程为什么要同步使用两个线程对一个全局变量做累加,从0加到10,所以只要每个线程累加到5就行。代码如下所示:#include<QApplication>#include<QThread>#include<QDebug>//定义共享资源intsharedValue=0;//定义一个线程类classMyThread:publicQThread{pub......
  • Qt - 多线程之线程的开始、暂停、恢复、停止
    示例1在Qt中,可以使用QThread的线程控制功能来暂停和恢复一个线程。这里是一个简单的例子: #include<QThread>#include<QDebug>classWorkerThread:publicQThread{voidrun()override{qDebug()<<"Threadisrunning";//执行一些任务......
  • 模拟线程池与异步方法调用查询接口优化
    问题:批量查询如何优化?entity实体类packagecom.itheima.alipay.prop;importlombok.Data;@DatapublicclassUserInfo{privateLonguserId;privateStringusername;privateintage;publicUserInfo(LonguserId,Stringusername,intage){......
  • 昊星新品发布会以三大主张、四大新品开创实验室安全新纪元
    【2024年11月19日,上海】    珠海昊星自动化系统有限公司(以下简称“昊星”)在2024年慕尼黑上海分析生化展上成功举办新品发布会。本次发布会深入展示了昊星在“匠心、创新、智慧化”方面的融合实践,彰显了昊星在实验室气流控制领域的技术实力和产品优势。多位行业专家出席......
  • Java面试之多线程&并发篇(6)
    前言本来想着给自己放松一下,刷刷博客,突然被几道面试题难倒!产生死锁的四个必要条件?如何避免死锁?线程池核心线程数怎么设置呢?Java线程池中队列常用类型有哪些?似乎有点模糊了,那就大概看一下面试题吧。好记性不如烂键盘***12万字的java面试题整理******java核心面试知识整理***......
  • C#单线程环境下实现每秒百万级调度
    C#并发控制框架:单线程环境下实现每秒百万级调度 阅读目录前言并发流程控制框架框架优势框架示例框架地址总结最后前言在工业自动化和机器视觉领域,对实时性、可靠性和效率的要求越来越高。为了满足这些需求,我们开发了一款专为工业自动化运动控制和机器视觉流程开发......
  • Linux线程退出、资源回收、资源清理的方法
    参考 Linux线程退出、资源回收、资源清理的方法_linux线程退出会释放哪些资源-CSDN博客 首先说明线程中要回收哪些资源,理解清楚了这点之后在思考资源回收的问题。1、子线程创建时从父线程copy出来的栈内存;线程退出有多种方式,如return,pthread_exit,pthread_cancel等;线......