多线程--06--多线程安全问题解决--03--无锁方式--原子类

一、基础概念

1.1 乐观锁与悲观锁

1.1.1 乐观锁

  • 乐观锁是无锁的实现,乐观是相对悲观锁而言的。
  • java中使用“自旋+CAS+volatile +unsafe类”实现乐观锁。
  • 乐观锁适用于竞争少、多核 CPU 的场景下(读多写少)。

1、如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响;

2、自旋需要CPU参与

1.1.2 悲观锁

           正如其名,具有强烈的独占和排他特性。它指的是对数据被外界修改持保守态度,为了避免同时被其他人修改,采取的是“先取锁再访问”的策略。synchronized、ReentrantLock都是悲观锁的典型例子。

1.2 自旋锁与阻塞锁

当一个线程拿不到锁的时候,有以下两种基本的等待策略:

  • 策略1:放弃CPU,进入阻塞状态,等待后续被唤醒,再重新被操作系统调度。
  • 策略2:不放弃CPU,空转,不断重试,也就是所谓的“自旋”。
  • 很显然,如果是单核的CPU,只能用策略1。因为如果不放弃CPU,那么其他线程无法运行,也就无法释放锁。但对于多CPU或者多核,策略2就很有用了,因为没有线程切换的开销。
  • 有一点要说明:这两种策略并不是互斥的,可以结合使用。如果拿不到锁,先自旋几圈;如果自旋还拿不到锁,再阻塞,synchronized关键字就是这样的实现策略。

二、原子类(Atomic类)基本介绍

java中,封装了“CAS+volatile +unsafe类 ”的操作,我们称为原子类,原子类有很多,都以Atomic开头,原子类对应的变量叫原子变量,原子类包括:

  • 原子整形类:AtomicInteger、AtomicLong、、AtomicBoolean;
  • 原子引用类:AtomicReference、AtomicStampedReference、AtomicMarkableReference;
  • AtomicReference:对引用类型做原子操作;
  • 原子类的ABA问题:从 A 改为 B 又改回 A 的情况,叫做ABA问题。

        原子整型类、AtomicReference不能感知ABA问题,需要使用AtomicStampedReference、AtomicMarkableReference解决ABA问题。

  • AtomicStampedReference:能知道被改了多少次,能彻底解决ABA问题。
  • AtomicMarkableReference:仅能感知到有没有被修改,能缓解ABA问题但不能彻底解决ABA问题。
  • 原子数组:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray;

对数组中的一个元素进行原子操作。注意:不是对整个数组进行原子操作

  • 字段更新器:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater;
  • 利用字段更新器,可以针对对象的属性进行原子操作。
  • 如果一个类是自己编写的,则可以在编写的时候把成员变量定义为Atomic类型。但如果是一个已经有的类,在不能更改其源代码的情况下,要想实现对其成员变量的原子操作,须用到字段更新器。
  • 字段更新器使用的限制条件:被处理的字段须被volatile修饰,否则会报错。

2.1 原子整型类AtomicInteger使用举例

package com.fuping3.atomic;

import java.util.concurrent.atomic.AtomicInteger;

public class Demo {
    private AtomicInteger atomic=new AtomicInteger(100);


    public void atomicTest(){
        while(true){               //自旋
            int expect=atomic.get();
            int update=expect-10;
            boolean result = atomic.compareAndSet(expect, update);//CAS
            if(result){
                System.out.println("更新成功");
                break;
            }
        }
    }

}

(1)如上代码运行流程如下图:

(2)原子类的CAS函数源码

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

原子类的CAS函数,其实是封装的Unsafe类中的一个native函数compareAndSwapInt,该函数有4个参数:

  • 第一个是对象(也就是AtomicInteger 对象);
  • 第二个是对象的成员变量偏移量(通过对象+偏移量,可以找到是对象的哪个属性);
  • 第三个参数expect是指变量的旧值(是读出来的值,写回去的时候,希望没有被其他线程修改,所以称为expect);
  • 第四个参数update是指变量的新值(修改过的,希望写入的值)。

当expect等于变量当前的值时,说明在修改的期间,没有其他线程对此变量进行过修改,所以可以成功写入,变量被更新为update,返回true,否则返回false。

Unsafe类仅提供三个CAS函数:compareAndSwapInt、compareAndSwapLong、compareAndSwapObject(引用类型CAS);

----》对boolean的支持是:将boolean转为int;

----》对double的支持是:依赖double类型提供的一对double类型和long类型互转的函数DoubleAdder

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值