多线程之四:无锁的简单介绍

 

整理自炼数成金

源码连接:

1. 无锁类的原理详解

    1.1.CAS

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

    1.2.CPU指令

        CAS操作不会涉及线程安全的问题,因为CAS操作是通过一条CPU指令进行的。

2. 无锁类的使用

    2.1.AtomicInteger

        2.1.1. 概述

            AtomicInteger 和Integer都是实现了Number接口。

    2.1.2. 主要方法

[java] view plain copy

  1. public final int get() //取得当前值  
  2.   
  3. public final void set(int newValue) //设置当前值  
  4.   
  5. public final int getAndSet(int newValue) //设置新值,并返回旧值  
  6.   
  7. public final boolean compareAndSet(int expect, int u) //如果当前值为expect,则设置为u  
  8.   
  9. public final int getAndIncrement() //当前值加1,返回旧值  
  10.   
  11. public final int getAndDecrement() //当前值减1,返回旧值  
  12.   
  13. public final int getAndAdd(int delta) //当前值增加delta,返回旧值  
  14.   
  15. public final int incrementAndGet() //当前值加1,返回新值  
  16.   
  17. public final int decrementAndGet() //当前值减1,返回新值  
  18.   
  19. public final int addAndGet(int delta) //当前值增加delta,返回新值  

 

    2.2.Unsafe

 

        2.2.1. 概述

        非安全的操作,比如:

        根据偏移量设置值

            park()

        底层的CAS操作

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

        2.2.2. 主要接口

[java] view plain copy

  1. //获得给定对象偏移量上的int值  
  2.   
  3. public native int getInt(Object o, long offset);  
  4.   
  5. //设置给定对象偏移量上的int值  
  6.   
  7. public native void putInt(Object o, long offset, int x);  
  8.   
  9. //获得字段在对象中的偏移量  
  10.   
  11. public native long objectFieldOffset(Field f);  
  12.   
  13. //设置给定对象的int值,使用volatile语义  
  14.   
  15. public native void putIntVolatile(Object o, long offset, int x);  
  16.   
  17. //获得给定对象对象的int值,使用volatile语义  
  18.   
  19. public native int getIntVolatile(Object o, long offset);  
  20.   
  21. //和putIntVolatile()一样,但是它要求被操作字段就是volatile类型的  
  22.   
  23. public native void putOrderedInt(Object o, long offset, int x);  

 

    2.3.AtomicReference

        2.3.1. 概述

        对引用进行修改

        是一个模板类,抽象化了数据类型

    2.3.2. 主要接口

        get()

        set(V)

        compareAndSet()

        getAndSet(V)

 

    2.4.AtomicStampedReference

        2.4.1. 概述

            我们会发现CAS操作还是有一个问题的

            比如之前的AtomicInteger的incrementAndGet方法

            假设当前value=1当某线程int current = get()执行后,切换到另一个线程,这个线程将1变成了2,然后又一个线程将2又变成了1。此时再切换到最开始的那个线程,由于value仍等于1,所以还是能执行CAS操作,当然加法是没有问题的,如果有些情况,对数据的状态敏感时,这样的过程就不被允许了。

        2.4.2. 主要接口

[java] view plain copy

  1. //比较设置 参数依次为:期望值 写入新值 期望时间戳 新时间戳  
  2.   
  3. public boolean compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp)  
  4.   
  5. //获得当前对象引用  
  6.   
  7. public V getReference()  
  8.   
  9. //获得当前时间戳  
  10.   
  11. public int getStamp()  
  12.   
  13. //设置当前对象引用和时间戳  
  14.   
  15. public void set(V newReference, int newStamp)  

 

    2.5.AtomicIntegerArray

 

        2.5.1. 概述

        支持无锁的数组

        2.5.2. 主要接口

[java] view plain copy

  1. //获得数组第i个下标的元素  
  2.   
  3. public final int get(int i)  
  4.   
  5. //获得数组的长度  
  6.   
  7. public final int length()  
  8.   
  9. //将数组第i个下标设置为newValue,并返回旧的值  
  10.   
  11. public final int getAndSet(int i, int newValue)  
  12.   
  13. //进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true  
  14.   
  15. public final boolean compareAndSet(int i, int expect, int update)  
  16.   
  17. //将第i个下标的元素加1  
  18.   
  19. public final int getAndIncrement(int i)  
  20.   
  21. //将第i个下标的元素减1  
  22.   
  23. public final int getAndDecrement(int i)  
  24.   
  25. //将第i个下标的元素增加delta(delta可以是负数)  
  26.   
  27. public final int getAndAdd(int i, int delta)  

 

    2.6.AtomicIntegerFieldUpdater

 

        2.6.1. 概述

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

        2.6.2. 主要接口

            AtomicIntegerFieldUpdater.newUpdater()

            incrementAndGet()

 

        2.6.3. 小说明

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值