java并发原子类AtomicBoolean解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 如果看过我之前的文章都知道这几天一直在更新java多线程这块的知识点,因为这块的知识点确实是比较多而且也别繁杂,因此对于java多线程基础知识点也会在两个多月的时间全部写完,这篇文章主要是针对java并发包下的一个原子类AtomicBoolean的讲解。

一、为什么使用AtomicBoolean?



我们平时一般都是使用的boolean来表示布尔变量,但是在多线程情况下boolean是非线程安全的。为什么是非线程安全的呢?我们看下面的这个例子:

private volatile Boolean flag = false;
publich void test() {
  synchronized(flag) {
    //去做其他的事:访问临界资源
    flag = !flag;
  }
}

大家可以看到,这个操作好像并没有什么问题,我们使用了synchronized关键字对flag对象进行上锁,这时候同一时刻就只能有一个线程去运行test方法中的代码了。如果你这样想那就大错特错了,其实此时synchronized对这块资源不起任何作用。为什么不起作用呢?我们来分析一下:


对于对象flag来说主要有两个值true和false。但是true和false却是两个不同的常量对象,也就是说synchronized关键字其实锁住的只是false对象,当下面test方法中把flag改为true就表示了另外一个对象。这就是为什么synchronized关键字失效的原因。


如何去解决这个问题呢?这时候我们的AtomicBoolean类就可以出马了,他可以很好的去解决这个问题。下面我们就来好好地分析一下AtomicBoolean类吧。


二、AtomicBoolean的使用


在一开始我们曾经也说到,在单线程中我们使用boolean是完全没有问题的,我们看如下代码:

public class Test6 implements Runnable{
    public static boolean flag = true;
    private String name;
    public Test6(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        if(flag) {
             System.out.println(name + ",起床");
             try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + ",上班");
             try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + ",下班");
            flag = !flag;
        }else {
             System.out.println(name + "想进来却进不来");
        }
    }
}

上面的代码功能是这样的,起床上班下班这三件事,一个人做完另外一个才可以继续做。这种boolean情况,在单线程状态下是安全的,但是在多线程条件下就是非线程安全的。我们可以创建两个线程去测试一下:

v2-d828133b50addde53c5dce9a7767d91a_1440w.jpg

原本我们想的是起床上班下班这三件事,一个人完成另外一个人再做,但是通过运行结果我们会发现,并列执行了。怎么才能实现我们的功能呢?我们再看下面的代码:

public class Test6 implements Runnable{
     private static AtomicBoolean flag = new AtomicBoolean(false);
     private String name;
    public Test6(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        if(flag.compareAndSet(false, true)) {
             System.out.println(name + ",起床");
             try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + ",上班");
             try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + ",下班");
            flag.set(false);
        }else {
             System.out.println(name + "想进来却进不来");
        }
    }
}

此时我们换成AtomicBoolean,在运行一下看看:

v2-fc0e35d390c52244097e0ab05a20cad4_1440w.jpg

我们会看到,此时执行的顺序就确定了张无忌想进来却进不来了。这就是其基本使用。下面我们分析一下其原理。


三、源码分析


想要了解其原理我们就必须要到源码中去看。在上面我们使用了compareAndSet方法,下面我们进入到这个方法中看看其源码实现:

/**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(boolean expect, boolean update) {
        int e = expect ? 1 : 0;
        int u = update ? 1 : 0;
        return unsafe.compareAndSwapInt(this, valueOffset, e, u);
    }


这个compareAndSet源码里面调用了unsafe的compareAndSwapInt方法,也就是使用了CAS机制,举一个我之前举的例子,这里expect和update是什么意思呢?也就是说我们现在的boolean如果不是except那就不更新,如果是我们预期的except,那就更新,更新的值就是update。也就是CAS原理,我们通过例子解释一下:


要给儿子订婚,你预期的儿媳妇是西施,但是儿子找的女朋友是貂蝉,你一看不是你预期的西施(except),一气之下就什么也不做,如果是预期的西施,那就给他们订婚。


注意:在这里我们还会发现一个问题,那就是我们的Boolean其实转化成了int类型,1表示true 0表示false。


这就是compareAndSet实现,底层使用的是CAS机制。当然还有很多其他的方法,我们可以看一下:

//返回当前值
public final boolean get() {
    return value != 0;
}
//先返回旧值,再设置新值
public final boolean getAndSet(boolean newValue) {
    boolean prev;
    do {
        prev = get();
    } while (!compareAndSet(prev, newValue));
    return prev;
}
//设置新值
public final void set(boolean newValue) {
    value = newValue ? 1 : 0;
}
//设置新值,该操作会让Java插入Store内存屏障,避免发生写操作重排序
public final void lazySet(boolean newValue) {
    int v = newValue ? 1 : 0;
    unsafe.putOrderedInt(this, valueOffset, v);
}

对于AtomicBoolean类其实是非常简单的。也是java并发机制中比较简单的类。这篇文章就先到这。如有问题还请指正。

相关文章
|
11天前
|
Java 编译器
Java 泛型详细解析
本文将带你详细解析 Java 泛型,了解泛型的原理、常见的使用方法以及泛型的局限性,让你对泛型有更深入的了解。
24 2
Java 泛型详细解析
|
12天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
40 12
|
9天前
|
存储 算法 Java
Java内存管理深度解析####
本文深入探讨了Java虚拟机(JVM)中的内存分配与垃圾回收机制,揭示了其高效管理内存的奥秘。文章首先概述了JVM内存模型,随后详细阐述了堆、栈、方法区等关键区域的作用及管理策略。在垃圾回收部分,重点介绍了标记-清除、复制算法、标记-整理等多种回收算法的工作原理及其适用场景,并通过实际案例分析了不同GC策略对应用性能的影响。对于开发者而言,理解这些原理有助于编写出更加高效、稳定的Java应用程序。 ####
|
9天前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
11天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
14天前
|
存储 缓存 监控
Java中的线程池深度解析####
本文深入探讨了Java并发编程中的核心组件——线程池,从其基本概念、工作原理、核心参数解析到应用场景与最佳实践,全方位剖析了线程池在提升应用性能、资源管理和任务调度方面的重要作用。通过实例演示和性能对比,揭示合理配置线程池对于构建高效Java应用的关键意义。 ####
|
12天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
10天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
12天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
6天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####

推荐镜像

更多