我在 JUC 的门口望了望,先瞥见的是 AtomicBoolean

写在前面的话

为什么单独写这篇文章?
因为在面试的过程中偶尔会被问到 volatile 关键字,自然而然就会说到可见性和原子性,紧接着就会聊到 i++ 的问题,然后就到了原子操作的一些类。
因此,我就挑了 JUC 下 atomic 包中的第一个类 AtomicBoolean 进行举例,希望通过本文达成两个目的:
1.首先,JUC 包下的源码太多了,我先挑一个最简单的练练手给自己攒点信心,为后面看其他核心代码做准备。
2.希望通过对 AtomicBoolean 的学习再复习一下线程安全的一些基础知识,通过简单的代码强化一下潜意识,也是为后面深入 JUC 做准备。

本文的优先级怎么样?
个人觉得,本文的优先级一般,对 AtomicBoolean 的学习性价比不高,顺着文章看完就差不多了,不用过于深入细节。

源码解读

先来看一下本文内容的范围,下图是 JDK1.8 JUC 包的目录结构,也就是java.util.concurrent.atomic.AtomicBoolean
在这里插入图片描述


关键变量

private volatile int value;

AtomicBoolean 的成员变量就一个 value,核心点也就是 volatile 关键字,快速回忆一下 volatile 关键字的作用:保证可见性,不保证原子性。
回忆到这就可以了,先别发散开来,不然一进到指令的实现原理又是大半个小时没了。


方法总览

先总览一下 AtomicBoolean 的所有变量和方法 ,可以看到其实很少,我们重点看下那些写操作相关的方法是线程安全的。
在这里插入图片描述


构造方法

/**  
 * Creates a new {@code AtomicBoolean} with the given initial value.  
 * * @param initialValue the initial value  
 */public AtomicBoolean(boolean initialValue) {
     
    value = initialValue ? 1 : 0;  
}  
  
/**  
 * Creates a new {@code AtomicBoolean} with initial value {@code false}.  
 */public AtomicBoolean() {
     
}

构造方法比较常规,就是初始化 value 值,因为是 boolean 类型的变量,所以只需要 0 和 1 区分 false 和 true,无参的话默认就是 false。


常规 get() 和 set() 方法

常规的 get set 方法确实很常规,所以这两个方法并不能保证并发场景下的原子性!一开始我真的觉得只要是 atomic 的下的所有方法都是原子的。

public final boolean get() {
     
    return value != 0;  
}

public final void set(boolean newValue) {
     
    value = newValue ? 1 : 0;  
}

getAndSet() 方法

getAndSet 方法才是保证了原子性的,可以清晰的看到内部其实是一个 do while 循环,不断的通过 compareAndSet 方法进行赋值,直到成功为止,返回 prev 旧值。

public final boolean getAndSet(boolean newValue) {
     
    boolean prev;  
    do {
     
        prev = get();  
    } while (!compareAndSet(prev, newValue));  
    return prev;  
}

lazySet() 方法

除了 set() 方法之外还提供了一个 lazySet() 方法,有什么区别呢?
lazySet 中调用的是 unsafe.putOrderedInt() 方法,该方法也是赋值操作,但是它不保证立即对其他线程可见,也就是不确保可见性。
按照我个人的理解,putOrderedInt 方法是没有内存屏障的。毕竟内存屏障本身是一个性能损耗,阻断了重排序的优化,但不是所有场景下我们都需要可见性。因此,在部分场景下可以使用更轻量的 lazySet() 方法。
实际上,工作中我从来没用到过,了解一下就行了。。。

public final void lazySet(boolean newValue) {
     
    int v = newValue ? 1 : 0;  
    unsafe.putOrderedInt(this, valueOffset, v);  
}

compareAndSet() 方法

compareAndSet 内部直接使用 unsafe 类下的 compareAndSwapInt 实现 CAS 写入。

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

随便提一下,还有一个 weakCompareAndSet 方法。这个方法的实现和 compareAndSet 是一模一样的,我第一次看的时候我尬住了,既然一模一样的为啥再来一遍,方法名上还加了个 weak 前缀。
后来才发现原来 compareAndSet 方法使用了 final 修饰,而 compareAndSet 没有。脑子里快速回忆下 final 关键字的作用,或许就是为其他的实现留了一个口子吧,这里我也没去细究,要是有想法的朋友可以告诉我一下~

public boolean weakCompareAndSet(boolean expect, boolean update) {
     
    int e = expect ? 1 : 0;  
    int u = update ? 1 : 0;  
    return unsafe.compareAndSwapInt(this, valueOffset, e, u);  
}

一些规划

至此,关于 AtomicBoolean 的一些知识点就介绍完了,相信大家能够感受到本文几乎什么难点都没讲,只是站在 JUC 的路口瞥了一眼,更多的是在强化对于基础知识的潜意识。
JUC 里面的内容量实在是大多了,但在我的实际工作中常用的屈指可数,所以我后续只准备挑个别常用的 and 面试常问的进行深入学习,当然也是因为我实在是厌恶看源码也不擅长画各种图,所以只想从实用性价比这两个方向去花时间。接下来的规划大概是:

  1. 把线程安全的集合完结了,已经完成了 HashMap/HashTable/ConcurrentHashMap 这三个,可能还会补充一个 arrayList 和 queue 相关的。
  2. locks 包下的重点类,如 ReentrantLock 的原理和使用,这部分是为后面的各种锁和进JVM做准备。
  3. Executor 多线程这一系列,也是日常开发中最常用的一部分了,会重点梳理下我的真实体验,但应该不会涉及最佳配置这一块了,因为业务场景各异,没有最佳,容易坑人。
  4. 真实业务场景下对 CyclicBarrier 和 CountDownLatch 的使用。因为曾经被问到过,在工作中也写过 2 次,所以还是单独拿出来过一下。

此外,关于 Usafe 类的细节我不准备展开了,有如下几个原因吧:

  1. 网上有很多已经讲的很好的博客了,我本身也不擅长扒拉源码,就不献丑了。
  2. 实际工作中我个人从来没直接使用过 Unsafe 类,我也坚决反对在业务代码里用它,所以我觉得系统性去学它的性价比极低,遇到的时候再网上查一下吧。当然了,为了面试没办法就看看。。。

关联阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-05 20:36:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-05 20:36:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-05 20:36:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-05 20:36:01       18 阅读

热门阅读

  1. 游戏如何选择服务器

    2024-02-05 20:36:01       25 阅读
  2. spring aop实现接口超时处理组件

    2024-02-05 20:36:01       31 阅读
  3. vue3.x 英文转换成简体中文

    2024-02-05 20:36:01       31 阅读
  4. AI智能语音机器人安装方法

    2024-02-05 20:36:01       28 阅读
  5. 【CSS transition(过渡效果)——详解】

    2024-02-05 20:36:01       28 阅读
  6. Python 泛型

    2024-02-05 20:36:01       28 阅读
  7. OpenGL的着色器内存访问

    2024-02-05 20:36:01       30 阅读