atomic 包(一)

1. 整体框架

这里写图片描述

2. 为什么提出 atomic 包

在多线程环境下编程的时候我们总是不可避免的会遇到线程安全的问题,这种问题的出现主要是由于多个线程需要同时操作共享资源,对共享资源进行更新,而更新操作一般来说都不是原子的,更新操作主要分为三个部分:读取、更新、写入,当一个线程 A 在操作共享资源的时候首先将共享资源的原始值读取到内存中,然后执行更新操作,之后再重新写入磁盘中。因为这个过程不是原子的,所以在 A 线程执行更新操作的时候,B 线程可能会执行同样的操作:读取、更新、写入, 这样就可能造成 B 线程读取的数据并不是最新的,A 的更新操作 B 线程可能没有读取到。导致了线程安全问题。

当这种情况发生的时候,一般的思路都是用同步或者锁来解决。Java 本身提供了 synchronized 关键字可以保证整个更新操作的原子性、可见性,Java 的 Lock 机制也可以轻松的实现同样的操作。

那么除了这两种方式外还有没有其它的解决方案呢?答案是肯定的,JDK 1.5 提供了 java.util.concurrent.atomic 包,提供了 12 个原子类,他们可以完成 4 种不同数据类型的更新操作,包括基本类型的更新、数组类的更新、引用类型和字段的更新。

不同于同步和锁机制的是,原子类的更新操作是在 CPU 级别实现的,对于应用程序来说,原子变量的更新操作就是原子的,我们下面通过简单的实验验证这个结论。

public class AtomicIntegerTest {
    AtomicInteger atomicCounter = new AtomicInteger();

    Integer intCounter = new Integer(0);

    private final int EXECUTE_COUNT = 10000;

  ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 10, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

    public void incr()
    {
        atomicCounter.getAndIncrement();
        intCounter ++;
    }

    @Test
    @SneakyThrows
    public void testAtomicIncr()
    {
        for (int i=0; i<EXECUTE_COUNT; i++)
        {
            executor.execute(() -> incr());
        }
        TimeUnit.SECONDS.sleep(1);
        System.out.println(atomicCounter);
        System.out.println(intCounter);
    }
}

这里写图片描述

执行上述单元测试,很容易得到验证结论的正确性,当使用 Integer 变量执行 ++ 操作的时候,这个部分对于 CPU 来说不是原子的,而是会分成三个步骤去执行,所以执行了 10000 次操作之后,并没有得到预期的结果,而使用 AtomicInteger 变量执行 getAndIncrement() 操作则可以保证 CPU 层面的原子性,最后得到预期的结果。
说到这里,其实我们只是知道在使用 atomic 包的时候可以保证更新的原子性,那么究竟为什么我们需要使用 atomic 包呢,使用 synchronized 同步和 Lock 机制还不够好吗?这里我们通过表格来对三者做一个横向的对比,方便大家对其产生更加深入的理解。

这里写图片描述

说到这里我们知道 atomic 包其实也是 Java 提供的一种实现同步的方式,它的性能在资源竞争激烈的时候依然有所保证,而且是在 CPU 层面实现,编码相对容易,对于大多数编写业务逻辑的 RD 来说
这部分功能可能会很少用到,但是这部分功能在 JDK 并发包中有大量的应用,熟练的掌握有助于我们快速的理解更多的源代码,而且如果有编写线程安全程序的需求,这种方式也是一个不错的选择,下面一个部分我们分别从 JDK 内部应用和个人应用程序两个角度学习使用 atomic 包中各个类的功能。

3. 如何使用 atomic 包
1> 内部应用:描述 JDK 内部是如何使用 atomic 包的功能保证线程安全的。
后文待续
2> 个人应用:如何在自己的应用程序中编写无等待、无锁定的高性能算法。
后文待续

4. 基本概念讲解
1> 原语(primitives): 不可中断的过程,操作系统的概念。
2> CAS
属于硬件同步原语,处理器级别的实现,应用程序可以直接使用,用来保证线程安全,主要包含三个操作数:
V:内存位置
A:预设原值(expected)
B:新值(update)
如果内存位置处的值与预设原值一样,那么把预设原值更改为新值。有点儿像数据库的乐观锁。
update A = C where A = B

从 Java5 开始,我们开始可以编写高性能的并发程序,Java5 提供的公共原子变量类让编写无等待、无锁定程序成为可能。

5. 引用

Java中的Atomic包使用指南:http://ifeve.com/java-atomic/
流行的原子:https://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html
java 5.0 锁机制:https://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html
Barging Lock: https://webkit.org/blog/6161/locking-in-webkit/
JDK 5.0 中更灵活、更具可伸缩性的锁定机制:https://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值