java高并发程序设计(三)无锁

无锁类原理详解

概念:

线程的无障碍的运行。

允许线程自由进入临界区,出现数据竞争时,保证一个胜出。

相对无障碍来讲比较切实可行的方案。

原理:

compare and swop算法

CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的 变量,E表示预期值,N表示新值。仅当V 值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么 都不做。最后,CAS返回当前V的真实值。CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成 操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程 不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS 操作即时没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。 

(概括:每个线程进来竞争数据的时候都给个期望值,如果期望值跟数据实际值是相等的,那么数据没被其它线程修改过,说明数据是安全的,那么这个线程就竞争成功,允许进行数据操作,     如果跟期望值不相等那么就会判定为竞争失败,且不会被挂起允许继续尝试,也允许线程放弃。)

(参考)CPU指令:

cmpxchg
/*
accumulator = AL, AX, or EAX, depending on whether
a byte, word, or doubleword comparison is being performed
*/
if(accumulator == Destination) {
ZF = 1;
Destination = Source;
}
else {
ZF = 0;
accumulator = Destination;
}
/*逻辑解析:
如果 寄存器值==目标值
则: 
   设置跳转标记,  原始数据设置为目标值
否则:
   不设置跳转标记 , 寄存器值  = 目标值(下次竞争数据)
*/复制代码

无锁相对有锁

有锁的情况下会导致线程被挂起,进入临界区之前也会被系统挂起,一旦线程被挂起就会导致性能下降(一旦被挂起再次做上下文交换会浪费掉80000个系统周期)。

无锁不会挂起,只会让线程做重试操作一次,也就消耗了几条指令而已(一个指令=一个系统周期)

无锁的操作方式比阻塞性能上好很多。

 无锁类的使用

 AtomicInteger(无锁整形)

继承了number,        有序列化接口

底层也是使用CAS算法(比较交换实例)完成的。

所有的 操作都是针对这个value属性,AtomicInteger是他的包装而已。

主要接口

public final int get() //取得当前值

 public final void set(int newValue) //设置当前值 

public final int getAndSet(int newValue) //设置新值,并返回旧值 

 public final boolean compareAndSet(int expect, int u) //如果当前值为expect,则设置u

public final int getAndIncrement() //当前值加1,返回旧值 

public final int getAndDecrement() //当前值减1,返回旧值

 public final int getAndAdd(int delta) //当前值增加delta,返回旧值 

public final int incrementAndGet() //当前值加1,返回新值

 public final int decrementAndGet() //当前值减1,返回新值 

public final int addAndGet(int delta) //当前值增加delta,返回新值

核心方法compareAndSet源码

/**
 * Atomically sets the value to the given updated value
 * if the current value {@code ==} the expected value.
 *
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful. False return indicates that
 * the actual value was not equal to the expected value.
 */
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
复制代码

如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。

参数:
expect - 预期值
update - 新值
返回:
如果成功,则返回 true。返回 False 指示实际值与预期值不相等。

unsafe操作   不安全的,  因为用了类似于C的指针指针样的东西。

getAndIncrement方法:

/**
 * Atomically increments by one the current value.
 *
 * @return the previous value
 */
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}复制代码
以原子方式将当前值减 1。

返回:
以前的值

Unsafe 

.概述

 非安全的操作,

比如: 

根据偏移量设置值

 park() 

底层的CAS操作 

非公开API,在不同版本的JDK中, 可能有较大差异 

.主要接口

 //获得给定对象偏移量上的int值

 public native int getInt(Object o, long offset); 

//设置给定对象偏移量上的int值

 public native void putInt(Object o, long offset, int x);

 //获得字段在对象中的偏移量

 public native long objectFieldOffset(Field f);

 //设置给定对象的int值,使用volatile语义

 public native void putIntVolatile(Object o, long offset, int x); 

//获得给定对象对象的int值,使用volatile语义

 public native int getIntVolatile(Object o, long offset); 

//和putIntVolatile()一样,但是它要求被操作字段就是volatile类型的

 public native void putOrderedInt(Object o, long offset, int x);

AtomicReference类

 概述

 对引用进行修改 是一个模板类,抽象化了数据类型 

主要接口

 get()

 set(V) 

compareAndSet() 

getAndSet(V) 


AtomicIntege类r使用实例:

package com.imooc.multithreading;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {
    static AtomicInteger atomicInteger = new AtomicInteger();
    public static class Addthread implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++){
                atomicInteger.incrementAndGet();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread[] thread = new Thread[10];
        for (int i=0;i<10;i++){
            thread[i]=new Thread(new Addthread());
        }
        for (int i=0;i<10;i++){
            thread[i].start();
        }
        for (int i=0;i<10;i++){
            thread[i].join();
        }
        System.out.println(atomicInteger);
    }
}复制代码

运行结果:


预期结果无误,说明是线程安全的

AtomicReference类使用实例:

package com.imooc.multithreading;

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceDemo {
    public final static AtomicReference<String> atomicReference = new AtomicReference<>("abc");

    public static void main(String[] args) {
        for (int i=0;i<10;i++){
            final int num = i;
            new Thread(){
                @Override
                public void run() {
                    try {
                        Thread.sleep(Math.abs((int)(Math.random()*100)));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(atomicReference.compareAndSet("abc","def")){
                        System.out.println("线程"+Thread.currentThread().getId()+"竞争成功并修改了值");
                    } else{
                        System.out.println("线程"+Thread.currentThread().getId()+"竞争失败");
                    }
                }
            }.start();
        }
    }
}复制代码

运行结果:


只有一个线程胜出,其余的因为,结果被修改预期值对不上,所以竞争失败,达到预期结果,所以AtomicReference类也是线程安全的。


AtomicStampedReference(原子标记参考)

概述

 ABA问题 

给操作对象加上标记,  记录这个对象的历史操作,     保证业务逻辑的唯一性

主要接口 

//比较设置 参数依次为:期望值 写入新值 期望时间戳 新时间戳 

public boolean compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp) 

//获得当前对象引用 

public V getReference() 

//获得当前时间戳 

public int getStamp() 

//设置当前对象引用和时间戳 

public void set(V newReference, int newStamp) 

AtomicIntegerArray 

概述 

支持无锁的数组 

主要接口 

//获得数组第i个下标的元素

 public final int get(int i) 

//获得数组的长度 

public final int length() 

//将数组第i个下标设置为newValue,并返回旧的值 

public final int getAndSet(int i, int newValue) 

//进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true 

public final boolean compareAndSet(int i, int expect, int update) 

//将第i个下标的元素加1 

public final int getAndIncrement(int i) 

//将第i个下标的元素减1

 public final int getAndDecrement(int i) 

//将第i个下标的元素增加delta(delta可以是负数)

 public final int getAndAdd(int i, int delta) 

使用示例


跟预期结果一致, 线程安全。

AtomicIntegerFieldUpdater(整数字段更新)

概述 让普通变量也享受原子操作 

主要接口 

//工厂方法,构建需要用到变量的实例

AtomicIntegerFieldUpdater.newUpdater(Class,“变量名”)  

 incrementAndGet() 

小说明

 1. Updater只能修改它可见范围内的变量。因为Updater使用反射得到这个变量。如果变量不可见,就会出错。 比如如果score申明为private,就是不可行的。

 2. 为了确保变量被正确的读取,它必须是volatile类型的。如果我们原有代码中未申明这个类型,那么简单得 申明一下就行,这不会引起什么问题。

3. 由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此,它不支持static字段(Unsafe. objectFieldOffset()不支持静态变量)。

示例:



与AtomicInteger类结果一指,说明数据操作功能没问题且数据安全。


转载于:https://juejin.im/post/5ba2353de51d450e950fe672

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值