原子类
原子类的作用:保证一个操作能够在多线程的情况下是不可中断的、不可分隔的,Java中的原子类存放与java.util.concurrent.atomic
包中
相对于锁有如下优势:
- 粒度更细:原子变量的竞争范围是变量级别
- 效率较高:除啦在高度竞争的情况下,使用原子类的效率会比使用锁的效率更高
基本类型原子类
AtomicInteger
AtomicLong
AtomicBoolean
以AtomicInteger
为例有以下方法
方法名 | 描述 |
---|---|
public final int get() |
获取当前值 |
public final void set(int newValue) |
设置当前值 |
public final int getAndSet(int newValue) |
获取当前值,并同时设置新值 |
public final int getAndIncrement() |
获取当前值,并同时自增1 |
public final int getAndDecrement() |
获取当前值,并同时自减1 |
public final int getAndAdd(int delta) |
获取当前值,并同时加指定数 |
public final boolean compareAndSet(int expectedValue, int newValue) |
若当前值的是expectedValue 预期值,则以原子方式将该值设置为newValue 新值,并返回是否更新成功 |
数组类型原子类
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
以AtomicIntegerArray
为例有以下方法:构造方法传入数组长度,此时时数组中都是不同类型的0值
方法名 | 描述 |
---|---|
public final int length() |
获取数组长度 |
public final int get(int i) |
获取i 索引下的元素 |
public final void set(int i, int newValue) |
设置i 索引下的元素 |
public final int getAndSet(int i, int newValue) |
获取i 索引下的元素,并同时设置新值 |
public final int getAndIncrement(int i) |
获取i 索引下值,并同时自增1 |
public final int getAndDecrement(int i) |
获取i 索引下值,并同时自减1 |
public final int getAndAdd(int i, int delta) |
获取i 索引下值,并同时加指定数 |
public final boolean compareAndSet(int i, int expectedValue, int newValue) |
若i 索引下的值是expectedValue 预期值,则以原子方式将该值设置为newValue 新值,并返回是否更新成功 |
引用类型原子类
AtomicReference
AtomicStampedReference
AtomicMarkableReference
以AtomicReference
为例有以下方法
方法名 | 描述 |
---|---|
public final boolean compareAndSet(V expectedValue, V newValue) |
若当前值的是expectedValue 预期值,则以原子方式将该值设置为newValue 新值,并返回是否更新成功 |
升级类型原子类
AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater
使用升级原子类的newUpdater()
静态方法,传入要升级的Class对象,以及该类中的要升级的变量,也就是说该方法使用的是反射机制创建对象,所以要升级的变量必须要有以下特点
- 不能是不可见(
private
)的变量 - 不能是静态变量
- 必须是
volatile
修饰的变量
该方法会返回一个将该变量升级成原子类的变量,就会具有下面方法:这些方法都需要首先将实例对象传入
方法名 | 描述 |
---|---|
public int get(T obj) |
获取当前值 |
public void set(T obj, int newValue) |
设置当前值 |
public int getAndSet(T obj, int newValue) |
获取当前值,并同时设置新值 |
public int getAndIncrement(T obj) |
获取当前值,并同时自增1 |
public int getAndDecrement(T obj) |
获取当前值,并同时自减1 |
public int getAndAdd(T obj, int delta) |
获取当前值,并同时加指定数 |
public boolean compareAndSet(T obj, int expect, int update) |
若当前值的是expectedValue 预期值,则以原子方式将该值设置为newValue 新值,并返回是否更新成功 |
class A {
public volatile int a;
}
public class Main {
public static AtomicIntegerFieldUpdater<A> updaterA = AtomicIntegerFieldUpdater
.newUpdater(A.class, "a"); //传入要升级的类的Class对象和属性名
public static void main(String[] args) throws InterruptedException {
A a = new A();
Runnable runnable = () -> {
for (int i = 0; i < 1000; i++) {
updaterA.getAndIncrement(a); //使用升级方法时需要传入对象,类似反射的方式
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(a.a);
}
}
累加器
Adder累加器
LongAdder
DoubleAdder
方法名 | 描述 |
---|---|
public void add(long x) |
加指定值 |
public void increment() |
自增1 |
public void decrement() |
自减1 |
public long sum() |
最后汇总求和,并返回 |
JDK8引入的,在高并发下比AtomicLong
效率高,效率的提升本质上是空间换时间,具体如下
LongAdder
在竞争激烈的情况下,每个线程对应到Cell数组上进行修改(相当于每个线程都有会存储一份独立的变量副本,最后进行多线程的计算汇总),使用hash值对应降低了冲突概率,从而提高了并发性能,是多段锁的理念;当竞争不激烈的情况下,所有线程对应同一个base变量上,与AtomicLong
效率差不多- 缺点:若在计算汇总时进行了变量修改,可能结果不一定精确,因为
sum()
方法不是线程安全的
- 缺点:若在计算汇总时进行了变量修改,可能结果不一定精确,因为
AtomicLong
在竞争激烈的情况下,每一次修改都要从线程工作内存向主内存中进行flush
和refresh
所以导致耗费资源
public class Main {
public static LongAdder longAdder = new LongAdder();
public static AtomicLong atomicLong = new AtomicLong();
public static void LongAdderTest() throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis(); //起始时间
for (int i = 0; i < 10000; i++) {
service.execute(() -> {
for (int j = 0; j < 10000; j++) {
longAdder.increment(); //自增1
}
});
}
service.shutdown();
if (service.awaitTermination(Integer.MAX_VALUE, TimeUnit.MILLISECONDS)) { //主线程等待线程池任务执行结束
long end = System.currentTimeMillis(); //结束时间
System.out.print(longAdder.sum() + ","); //将所有线程的计算进行汇总
System.out.println("LongAdder耗时:" + (end - start));
}
}
public static void AtomicLongTest() throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis(); //起始时间
for (int i = 0; i < 10000; i++) {
service.execute(() -> {
for (int j = 0; j < 10000; j++) {
atomicLong.getAndIncrement(); //自增1
}
});
}
service.shutdown();
if (service.awaitTermination(Integer.MAX_VALUE, TimeUnit.MILLISECONDS)) { //主线程等待线程池任务执行结束
long end = System.currentTimeMillis(); //结束时间
System.out.print(atomicLong.get() + ","); //查看最后的值
System.out.println("AtomicLong耗时:" + (end - start));
}
}
public static void main(String[] args) throws InterruptedException {
LongAdderTest();
AtomicLongTest();
}
}
Accumulator累加器
LongAccumulator
DoubleAccumulator
相对于LongAdder
更加通用,用法类似与归纳,可使用多线程实现并行计算
public static void main(String[] args) throws InterruptedException {
LongAccumulator longAdder = new LongAccumulator((current, last) -> {
return current + last; //current是当前值 last是上一次的值 返回计算后的值
}, 100); //初始值
longAdder.accumulate(1); //累计值
System.out.println(longAdder.get()); //101
}
Comments NOTHING