【一】关于java.util.concurrent包下的并发类(atomic)

并发类包除了java.util.concurrent之外,还有java.util.concurrent.atomic和java.util.concurrent.lock.
java.util.concurrent中主要是一些关于集合框架的并发实现,例如ConcurrentHashMap。多线程任务执行类:Callable(被执行的任务)、Executor(执行任务)和Future(异步提交任务的返回数据)等等。以及线程管理类:例如CyclicBarrier、CountDownLatch和Exchanger。

java.util.concurrent.atomic实现了原子化操作的数据类型:例如AtomicBoolean、AtomicInteger等等。

java.util.concurrent.lock实现了并发操作中的几种类型的锁。

 这一块LZ并不熟悉,故而会分若干篇文章进行归纳总结。应该总结的内容也比较流于表面。大体的意思能弄明白能描述清楚就已经很好了。

1、atomic包下的相关类
这个包下的类大体上通过了解volatile关键字和CAS算法大体就可以了。
以AtomicInteger举例,它存在一个被volatile修饰的变量value。这个value在初始化时会被设置为initialValue或者默认为0。而此时这个value就是后文所要提到的原来的值。同时存在一个final类型的静态变量valueOffset,这个变量会在随后的静态代码块中被赋值(通过sun.misc包下的Unsafe类获取到原来的值的偏移地址)。

被volatile修饰的变量,其存储于共享内存中,而非在各个线程的专属存储空间。即这个变量是被所有线程所共享的、可见的。每次读取的都是内存中该变量的最新值。在这个过程中涉及到内存屏障。所谓内存屏障(一个CPU指令)的作用就是告诉CPU和编译器在这个屏障之前的命令必须先执行完成,在屏障之后的命令必须后执行。另一个作用是强制更新一次不同CPU的缓存。故而,被volatile修饰的变量,其在写操作后会插入一个写屏障,在读操作前插入一个读屏障。这便意味着你完成了写操作之后,任意线程得到的都是最新的值,而且在你读取之前会保证所以之前的事均已发生,并且任何新的值都会被刷新到内存中。但是这里要注意,volatile是不能保证原子的可见性的,除了Long类型和Double类型除外。因为在32位的操作系统之中,CPU一次只能读取32位,而这两个类型均是64位,其读写操作会分为两步进行。

CAS算法:什么是CAS算法(compareAndSwap)?其基本思想是原本的值与期望的值进行比较,如若相等,则更新当前值为新值。不相等则不做任何事,但要返回原值。
下面引用了一个博主的简单的CAS实现:
public class SimulatedCAS {
    private int value;

    public synchronized int getValue() {
        return value;
    }

    public synchronized int compareAndSwap(int expectedValue, int newValue) {
        int oldValue = value;
        if (value == expectedValue)  //如若value和期望值相等
            value = newValue; //将newValue复制给value
        System.out.println("value" + value);
           return oldValue; //返回原来的值
}
}
public class CasCounter {
    private SimulatedCAS value = new SimulatedCAS();

    public int getValue() {
        return value.getValue();
    }

    public int increment() {
        int oldValue = value.getValue();
        System.out.println(oldValue);
        while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue) //value返回的值和获取到的值相同
            oldValue = value.getValue();

        return oldValue + 1; //返回原来的值+1
    }
    public static void main(String[] args) {
        CasCounter casCounter = new CasCounter();
        System.out.println(casCounter.increment());  //输出1
        System.out.println(casCounter.increment());/ /输出2
    }
}

上述例子基本阐明了CAS的基本思想。在AtomicInteger中,getAndIncrement方法便利用了CAS的思想,通过Unsafe的compareAndSwap比较期望的值和valueOffset内存地址指向的值是否一致,如若一致,则更新为newValue。

上述以AtomicInteger为例,简要的说明了一下volatile关键字以及CAS算法的基本思想。实际上在atomic包中,类似于AtomicBoolean、AtomicLong等类的实现方式与AtomicInteger基本一致。其均是用CAS算法实现getAndSet、Increment、decrement等等操作。

可以看到在整个atomic中不存在任何的锁对象,它保持原子性的方式是通过CAS,将原本有语言层次的阻塞性交给了CPU、硬件层面。从而使得操作更有效率。(具体如何没有详加了解)

猜你喜欢

转载自blog.csdn.net/qq_32302897/article/details/81006361