1、背景:java原生定义的八大基本数据类型是线程不安全的,毕竟没有任何封装,除非自己加上一些关键字或者锁…
2、考虑到java并发,解决一般性的并发问题,java官方在JUC的包内加了一些封装了基本类型的类,同时维护了他们一定程度上的线程安全
3、前奏:我们以前了解,java并发及多线程资源共享是存在隐患的,至于具体是什么这里不叙说,网上百度,教程多多。其中有一个关键字尤其重要,它就是 volatile
4、volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读(以原子的方式进行执行,具体更多了解去百度了解,这个东西的原理有那么复杂)
5、而JUC为我们提供了一系列封装volatile修饰的变量的类,下面一一讲解:
1、AtomicLong
看看这个类的源码
可以简单理解就是在为了达到线程安全给基本类型套了层皮,使其具有原子操作能力
一些较为常用的函数
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
public class TimeUtilsTest {
public static void main(String[] args){
// 实例化一个AtomicLong类实例,传入一个初始long类型的值
// 注意:无参构造时的初始值是 0
AtomicLong atomicLong = new AtomicLong(10);
// 获取这个被volatile修饰的long值
System.out.println("get():"+atomicLong.get());
// 四则运算,加法,并返回新的结果值
System.out.println("addAndGet():"+atomicLong.addAndGet(40));
//设置一个新的值,并且返回旧的值
System.out.println("getAndSet(): "+atomicLong.getAndSet(10));
//比较是否和所给的预期值一致,如果一致就修改为新的值
// 10 是预期值,如果value的值就是10,则将value值改为100,反之不变value的值,
//返回一个boolean值,表示value值是否和预期值一致
System.out.println("compareAndSet(): "+atomicLong.compareAndSet(10, 100));
//先获取当前value的值,再将当前值减一,返回是修改前的value值
System.out.println("getAndDecrement(): "+atomicLong.getAndDecrement());
//以原子的方式将value值减1再返回修改后的value值,原子方式是指:要么运行成功,要么运行失败!
System.out.println("decrementAndGet(): "+atomicLong.decrementAndGet());
//先获取当前value的值,再将当前值加一,返回是修改前的value值
System.out.println("getAndIncrement(): "+atomicLong.getAndIncrement());
//以原子的方式将value值加1再返回修改后的value值,原子方式是指:要么运行成功,要么运行失败!
System.out.println("incrementAndGet(): "+atomicLong.incrementAndGet());
}
}
运行结果截图:
PS: AtomicBoolean、AtomicInteger用法和上面类似,就不一一列举
但是对于AtomicInteger这个int的封装类,还是有一定注意一下,就是它的compareAndSet方法
底层方法是native修饰的(其他语言开发的,一般是c/c++),这个注意一下就可以,具体知识读者可以自行探索,下面给下简单的实例方法
AtomicBoolean atomicBoolean = new AtomicBoolean(true);
AtomicInteger atomicInteger = new AtomicInteger(20);
2、AtomicLongArray
我们看看这个类的源码
诶,发现了一个很奇怪的现象,这个类既然内部封装了线程操作安全的数组,可是也没见到数组是线程安全的,和我们一般创建的不是一样的吗?
其实啊,它这里是引用了一种新的java原生方法
private static final VarHandle AA
= MethodHandles.arrayElementVarHandle(long[].class);
就是这个VarHandle,这是java 9引入的一个类,这里就作简单介绍:)
- 用于更加细粒度的控制内存排序。在安全性、可用性
- 性能更优
- 可以与任何字段、数组元素或静态变量关联,支持在不同访问模型下对这些类型变量的访问,包括一些简单的 Read/Write 访问,volatile 类型的 Read/Write 访问,和 CAS(compare and swap)等
其实说这么多就是表达一点,为一个普通的属性赋值原子操作能力,而操作的对象自然就是普通的long数组了
本人太菜了,只了解这些,具体了解可以自行百度
1、两个有参构造方法(打错字了,是传入,(。・_・。)ノI’m sorry~)
常使用方法:
import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.*;
public class TimeUtilsTest {
public static void main(String[] args){
// 以下都简称这个类为数组
// 对long数组进行封装,使其变为按照原子性的线程安全类型的数据结构
AtomicLongArray array = new AtomicLongArray(new long[]{1,2,0,5});
// 获取内部数组的长度
System.out.println("length(): "+array.length());
//将数组下标为i的值修改一个新的值,返回类型是void,下标从0开始
array.set(0,100);
// 将数组下标为i的值和一个值相加, 并返回相加后的值
System.out.println("addAndGet(): "+array.addAndGet(2, 20));
// 第一个是下标,第二个是预期值,第三个是将要修改的新值
// 判断数组下标为i的值是否和预期值一致,如果一致就修改为新值
// 返回值是Boolean,代表是否和预期值一致
//该函数缩小是著名的CAS,其有效地说明了
// “我认为位置0应该包含值100;如果包含该值,则将10放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可"
System.out.println("compareAndSet(): "+array.compareAndSet(0, 100, 10));
// 获取数组下标为i的值
System.out.println("get(): "+array.get(0));
//将数组下标为i的值先取出,再讲数组下标为i的值减一,返回修改前的值
System.out.println("getAndDecrement(): "+array.getAndDecrement(0));
//以原子方式将数组下标为i的值先减一。再取出,返回修改后的值
System.out.println("decrementAndGet(): "+array.decrementAndGet(0));
//将数组下标为i的值先取出,再讲数组下标为i的值加一,返回修改前的值
System.out.println("getAndIncrement(): "+array.getAndIncrement(0));
//以原子方式将数组下标为i的值先加一。再取出,返回修改后的值
System.out.println("incrementAndGet(): "+array.incrementAndGet(0));
}
}
/*
总结一点:其实 ++i 和 i++ 或者- -,都不是原子操作
它内部都是通过CAS(compareAndSet)来实现的
*/
看下面源代码就晓得了
而Atomic的引用下次再更新,累死了,脑壳痛