AtomicBoolean

AtomicBoolean的使用

 

前言
JDK1.5之后的java.util.concurrent.atomic包里,多了一批原子处理类。AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference。主要用于在高并发环境下的高效程序处理,来帮助我们简化同步处理。

为什么使用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是完全没有问题的,我们看如下代码:

package com.sunhui.thread.CompletableFuture.Java8;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Description
 * @ClassName AtomicBooleanDemo
 * @Author SunHui
 * @Date 2021/7/15 4:17 下午
 */
public class AtomicBooleanDemo implements Runnable{

    private static boolean flag = true;
    private String name;
    
    public AtomicBooleanDemo(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 + "想进来却进不来");
        }
    }
    
    public static void main(String[] args) {
        AtomicBooleanDemo demo1 = new AtomicBooleanDemo("张三");
        AtomicBooleanDemo demo2 = new AtomicBooleanDemo("李四");
        new Thread(demo1).start();
        new Thread(demo2).start();
    }
}

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

在这里插入图片描述

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

package com.sunhui.thread.CompletableFuture.Java8;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description
 * @ClassName AtomicBooleanDemo
 * @Author SunHui
 * @Date 2021/7/15 4:17 下午
 */
public class AtomicBooleanDemo implements Runnable{
    
    private static AtomicBoolean flag = new AtomicBoolean(false);
    private String name;

    public AtomicBooleanDemo(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 + "想进来却进不来");
        }
    }

    public static void main(String[] args) {
        AtomicBooleanDemo demo1 = new AtomicBooleanDemo("张三");
        AtomicBooleanDemo demo2 = new AtomicBooleanDemo("李四");
        new Thread(demo1).start();
        new Thread(demo2).start();
    }
}

此时我们换成AtomicBoolean,在运行一下看看:在这里插入图片描述

 

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

AtomicBoolean源码分析

废话少说,直接上源码:

    /**
     * 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的compareAndSet方法,也就是使用了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并发机制中比较简单的类。这篇文章就先到这。如有问题还请指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值