设为首页 加入收藏

TOP

Java高并发之无锁与Atomic源码分析(一)
2018-06-09 10:07:57 】 浏览:430
Tags:Java 并发 Atomic 源码 分析

无锁即无障碍的运行, 所有线程都可以到达临界区, 接近于无等待.无锁采用CAS(compare and swap)算法来处理线程冲突, 其原理如下


CAS包含3个参数CAS(V,E,N).V表示要更新的变量, E表示预期值, N表示新值.


仅当V值等于E值时, 才会将V的值设为N, 如果V值和E值不同, 则说明已经有其他线程做了更新, 则当前线程什么


都不做. 最后, CAS返回当前V的真实值. CAS操作是抱着乐观的态度进行的, 它总是认为自己可以成功完成操作.


当多个线程同时使用CAS操作一个变量时, 只有一个会胜出, 并成功更新, 其余均会失败.失败的线程不会被挂起,


仅是被告知失败, 并且允许再次尝试, 当然也允许失败的线程放弃操作.基于这样的原理, CAS操作即时没有锁,


也可以发现其他线程对当前线程的干扰, 并进行恰当的处理.


CPU指令


另外, 虽然上述步骤繁多, 实际上CAS整一个操作过程是一个原子操作, 它是由一条CPU指令完成的,


从指令层保证操作可靠, 不会被多线程干扰.


无锁与volatile


无锁可以通过cas来保证原子性与线程安全, 他与volatile什么区别呢?


当给变量加了volatile关键字, 表示该变量对所有线程可见, 但不保证原子性.


以volatile i, i++为例, 分为以下四步:


其中前三步是线程不安全的, 可能其他线程会对i进行读写.


主要接口


// 取得当前值
public final int get()
// 设置当前值
public final void set(int newValue)
// 设置新值,并返回旧值
public final int getAndSet(int newValue)
// 如果当前值为expect,则设置为u
public final boolean compareAndSet(int expect, int u)
// 当前值加1,返回旧值
public final int getAndIncrement()
// 当前值减1,返回旧值
public final int getAndDecrement()
// 当前值增加delta,返回旧值
public final int getAndAdd(int delta)
// 当前值加1,返回新值
public final int incrementAndGet()
// 当前值减1,返回新值
public final int decrementAndGet()
// 当前值增加delta,返回新值
public final int addAndGet(int delta)


源码实现


// 封装了一个int对其加减
    private volatile int value;
    .......
    public final boolean compareAndSet(int expect, int update) {
    // 通过unsafe 基于CPU的CAS指令来实现, 可以认为无阻塞.
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    .......
    public final int getAndIncrement() {
        for (;;) {
        // 当前值
            int current = get();
        // 预期值
            int next = current + 1;
            if (compareAndSet(current, next)) {
        // 如果加成功了, 则返回当前值
                return current;
        }
        // 如果加失败了, 说明其他线程已经修改了数据, 与期望不相符,
        // 则继续无限循环, 直到成功. 这种乐观锁, 理论上只要等两三个时钟周期就可以设值成功
        // 相比于直接通过synchronized独占锁的方式操作int, 要大大节约等待时间.
        }
    }


Demo


使用10个线程打印0-10000, 最终得到结果10w.


 


import java.util.concurrent.atomic.AtomicInteger;


 


public class AtomicIntegerDemo {
    static AtomicInteger i = new AtomicInteger();


 


    public static class AddThread implements Runnable {
        public void run() {
            for (int k = 0; k < 10000; k++) {
                i.incrementAndGet();
            }
        }
    }


 


    public static void main(String[] args) throws InterruptedException {
        Thread[] ts = new Thread[10];
        for (int k = 0; k < 10; k++) {
            ts[k] = new Thread(new AddThread());
        }
        for (int k = 0; k < 10; k++) {
            ts[k].start();
        }
        for (int k = 0; k < 10; k++) {
            ts[k].join();
        }
        Sys
编程开发网

首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/10/10
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇JDK并发包详细总结 下一篇Java内存模型与指令重排

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

最新文章

热门文章

C 语言

C++基础

windows编程基础

linux编程基础

C/C++面试题目