了解Java多线程的可见性与有序性

了解Java多线程的可见性与有序性

可见性

在Java多线程中,可见性问题是指当多个线程访问共享数据时,其中一个线程对数据进行了修改,导致其他线程无法立即看到这个修改的结果。

原因

可见性问题的产生是因为java内存模型中存在主内存和工作内存的缓存机制,不同的线程可能会将共享数据拷贝到自己的工作内存中进行修改,修改后的结果,在没有及时写回主内存的情况下,其他线程是看不到的。

解决方案

Java提供了volatile关键字来保证多线程间共享变量的可见性。

被volatile修饰的变量,操作之后都会立即写入主内存,而其他线程在进行操作之前,会先访问主内存中的这个变量,从而保证操作都是对主内存的同一个变量进行的,因此能够避免了可见性问题的发生。

public class volatileDemo {

    public volatile boolean flag = false;
    public void setFlag(){
        flag = true;
    }
    public void printFlag() {
        System.out.println("flag:" + flag);
    }

    public static void main(String[] args) {
        final volatileDemo demo = new volatileDemo();
        new Thread(new Runnable() {
            public void run() {
                demo.setFlag();
            }
        }).start();
        new Thread(new Runnable() {
            public void run() {
                while(!demo.flag) {
                    System.out.println("flag is false.");
                    Thread.yield();
                }
                System.out.println("flag is true.");
            }
        }).start();
    }
}

示例说明

假设有两个线程T1、T2,并且他们共享了一个布尔类型的变量flag,初始值均为false。

在T1线程中,调用setFlag()方法将flag设置为true。

在T2线程中,通过自旋的方式(即反复执行一个简单的操作等待目标操作完成)来读取flag的值,如果值为false,就继续等待,一旦值为true,就输出flag is true.的语句。

如果没有使用volatile修饰flag的话,T2线程可能永远看不到T1线程所做的修改,因为T2线程所读取的flag是从自己的工作内存中获取的。而使用volatile修饰flag的话,相当于保证了T2线程所读取的flag是从主内存中获取的,因此能够保证可见性的问题。

有序性

Java内存模型中的有序性,是指JMM规定的代码执行顺序和程序员编写代码时期望的有序执行顺序不同的问题。

原因

由于JMM允许编译器和处理器对指令进行重排序的优化,因此可能会导致程序员编写的代码执行顺序和实际执行顺序不一致的问题。

例如,代码A中包含修改共享变量和判断共享变量的操作,如果编译器或处理器对代码进行重排序,可能会导致先判断共享变量再修改共享变量的情况出现,因此就会导致程序出现错误的结果。

解决方案

Java为程序员提供了很多手段来保证多线程的有序性:

synchronized

可以使用synchronized关键字来保证多线程的有序性,synchronized具有原子性、可见性、有序性等特性,能够避免编译器和处理器对指令进行重排序的优化。

volatile

被volatile修饰的变量能够保证线程之间的有序性,每个线程在访问volatile变量之前,都会先刷新本地内存中的变量,保证所有的线程都能够访问到最新的变量值。

final关键字

final关键字在多线程中也具有有序性,final域的初始化不会被重排序,并且对于所有线程可见,因此能够保持有序性。

public class Singleton {

    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;
    }
}

示例说明

在单例模式中,为了确保线程安全性,getInstance方法需要保证只能同时一个线程进入到创建实例的阶段。

如果不使用volatile修饰instance的话,可能会出现重排序的问题,比如初始化对象的时候分3个步骤:1)分配对象的内存空间;2)初始化对象;3)将对象指向刚分配的内存地址。但是由于2和3的顺序不需要保证,因此可能会出现先将对象指向地址,但是还没有初始化的情况,这样会导致其他访问instance的线程得到的是一个还没初始化的实例,从而导致程序出错。

而使用volatile修饰instance的话,能够避免这种重排序的问题,能够保证有序性的正确性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:了解Java多线程的可见性与有序性 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 详解Java多线程编程中线程的启动、中断或终止操作

    当我们创建了一个线程对象后,可以通过调用start()方法启动该线程。在Java多线程编程中,我们通常使用继承Thread类或实现Runnable接口的方式来创建一个线程。下面我将详细介绍线程的启动、中断和终止操作。 启动线程 继承Thread类的方式 创建Thread类的子类,重写它的run()方法; 创建该子类的实例; 调用该实例的start()方法以启…

    多线程 2023年5月17日
    00
  • 理论讲解python多进程并发编程

    理论讲解Python多进程并发编程 什么是多进程并发编程 多进程并发编程指的是在同一时间内,有多个进程同时运行,从而达到提高程序执行效率的目的。这种编程方式可以更好的利用多核CPU的能力,提高程序的计算能力和并发性。 如何实现多进程并发编程 Python提供了许多库来实现多进程并发编程,其中最常用的是multiprocessing库。在使用multiproc…

    多线程 2023年5月16日
    00
  • Java多线程之ThreadLocal浅析

    Java多线程之ThreadLocal浅析 ThreadLocal 是 Java 中的一个用于多线程编程的类库,它提供了一个线程局部变量,每一个线程都有自己独立的副本,可以对该变量进行读写操作,而且互不影响,解决了多线程环境下共享数据的问题。 使用 ThreadLocal 先看下 ThreadLocal 的使用方式: public class ThreadL…

    多线程 2023年5月17日
    00
  • PHP使用Pthread实现的多线程操作实例

    下面我将详细介绍如何使用 Pthread 实现 PHP 的多线程操作。 什么是 Pthread Pthread 是 PHP 中的一个扩展库,它支持 POSIX 线程(或称 Pthreads)操作,可以在同一进程中创建多个线程,从而实现并行处理和多线程并发执行等操作。 安装 Pthread 扩展 在使用 Pthread 扩展前,需要先安装该扩展。下面介绍 Pt…

    多线程 2023年5月17日
    00
  • 分析Go语言中CSP并发模型与Goroutine的基本使用

    分析Go语言中CSP并发模型与Goroutine的基本使用攻略 什么是CSP并发模型 CSP (Communicating Sequential Processes),即通信顺序进程,是一种并发计算模型。这种模型通过在进程之间传递消息来进行通信,而不是共享内存。在Go语言中,CSP并发模型采用channel(通道)来实现进程间通信(IPC)。 Gorouti…

    多线程 2023年5月17日
    00
  • java并发编程专题(十)—-(JUC原子类)基本类型详解

    一、介绍 在Java并发编程中,为了解决并发问题,我们一般都会使用锁,但是锁虽然能够解决并发问题,但也会带来额外的性能开销和代码复杂度。JUC原子类就是为了解决这个问题而生的。 JUC原子类提供了一些并发安全且具有原子性的操作,即这些操作在执行时,不会被其他线程的并发操作所干扰。JUC原子类是通过使用CAS(Compare And Swap)机制实现的,因此…

    多线程 2023年5月16日
    00
  • JavaScript多线程的实现方法

    JavaScript 是单线程的语言,这意味着整个程序只有一个执行线程,即只有一个代码段可以被执行。但是,为了提高性能和用户体验,有时候我们需要实现多任务并行执行,此时需要使用 JavaScript 中的多线程技术。 JavaScript 中实现多线程可以通过以下两种方法: Web Workers Web Workers(网络工作者)是一种运行在后台的 Ja…

    多线程 2023年5月17日
    00
  • Python并发编程线程消息通信机制详解

    Python并发编程线程消息通信机制详解 在Python并发编程中,线程之间通信是非常常见的场景,本文将详细讲解Python线程之间的消息通信机制,包括线程锁、事件、条件、队列等几种常见的机制。 线程锁 Python中的线程锁又称为互斥锁,用于限制多个线程访问同一共享资源时的冲突。下面是一个基本的示例: import threading x = 0 lock…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部