JUC-无锁并发(CAS/volatile)

知识储备

主内存与工作内存

Java内存模型的成员变量都存储在主内存(Main Memory)(也就是堆和方法区内存)中,此外每条线程都有自己的工作内存(Working Memory)(也就是栈内存)。

线程对成员变量操作: 从主内存中拷贝变量到自己的工作内存 > 对变量进行操作后 > 将操作后的值写入主内存。
在这里插入图片描述

volatile

特点:

  1. 可见性volatile 保证了变量每次修改都能立即同步到主内存,每次使用前立即从主内存刷新。保证每个线程每次使用拿到的都是最新值,而普通变量做不到这点。
  2. 有序性通过禁止指令重排序优化):volatile 通过设置 Java 内存屏障禁止重排序优化。(这会让volatile变量在写的过程中插入内存屏障指令,从而付出性能代价)

与普通变量区别:

  1. volatile每次读都必须去主内存中Load①
  2. volatile每次写都必须立即写到主内存Store②
  3. 线程对普通变量只会读一次

volatile变量读写并不是一个原子操作,volatile不具备原子性


完全安全的只有:synchronized 和 java.util.concurrent.atomic 中的原子类

在这里插入图片描述

CAS算法

CAS全称是Compare and Swap,即比较并交换,是通过原子指令来实现多线程的同步功能,将获取存储在内存地址的原值和现值进行比较,只有当他们相等时,交换指定的预期值和内存中的值,这个操作是原子操作,若不相等,则返回false。

CAS是一种无锁算法,有3个关键操作数,内存地址,旧的内存中预期值,要更新的新值,当内存值和旧的内存中预期值相等时,将内存中的值更新为新值。

乐观锁与悲观锁
  1. CAS属于乐观锁(Optimistic Locking),乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就利用循环重试,直到成功为止。

  2. synchronized是悲观锁(Pressimistic Locking),被一个线程拿到锁之后,其他线程必须等待该线程释放锁,性能较差。

java.util.concurrent.atomic 中的原子类

Unsafe类是CAS实现的核心,方法由C++编写实现原子操作。
而java.util.concurrent.atomic 中的原子类就是Unsafe类的各种包装类。
在这里插入图片描述

在这里插入图片描述

以AtomicInteger为例重要方法

绝对的线程安全

  1. 自增1后返回值:count.incrementAndGet();
  2. 自减1后返回值:count.decrementAndGet();
  3. 返回值后自减1:count.getAndDecrement();
  4. 返回值后自加1:count.getAndIncrement();
  5. 返回值后加上delta:count.getAndAdd(int delta);
  6. boolean compareAndSet(int expect, int update) : 如果当前值等于预期值(expect),则以原子方式将该值设置为输入值(update)【这个方法叫CAS操作】
结合for循环的重要实现方式

在这里插入图片描述

原子更改对象中的volatile属性:Atomic…FieldUpdater

在这里插入图片描述

原子读写一个对象:AtomicReference

在这里插入图片描述

CAS的ABA问题

在这里插入图片描述

解决办法:AtomicStampedReference 与 AtomicMarkableReference

但是:AtomicMarkableReference并不能很好的避免问题,因为它每次设置的是一个标志,而这个标志是boolean类型
在这里插入图片描述

AtomicStampedReference:

AtomicStampedReference原子类是一个带有时间戳的对象引用,在每次修改后,AtomicStampedReference不仅会设置新值而且还会记录更改的时间。
只是增加了一个时间戳对象,用法和临界变量一模一样!

//初始化时需要传入一个初始值和初始时间
private AtomicStampedReference tail = new AtomicStampedReference(0,0);

public boolean enqueue(Object item){
        int preTail = 0;
        int newTail = 0;
        int preTime = 0;
        int newTime = 0;

        for (;;){
            preTail = (Integer)tail.getReference();//获取此刻的变量值
            preTime = tail.getStamp();//获取获取此刻变量的时间值
			//修改获得值
            newTail = (preTail+1)%capacity;
            newTime = preTime + 1;

            if (newTail == head.get()) return false;
            if (tail.compareAndSet(preTail,newTail,preTime,newTime)){//验证变量值、变量时间是否为有被更改
                items[preTail] = item;
                return true;
            }
        }
    }

实战

注意点:

  1. 往往需要被操作的数据集合由 volatile 修饰保证可见性
  2. 而逐增、逐减的关键变量需要的是 CAS 原子操作
  3. 要在for循环中获得、操作、判断和存储,成功则直接return就可以。失败的话,循环再次获得…

具体实现模型:data01/src/data/queue

package data.queue;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 数组循环队列
 */

public class LoopArrayQueue {
    private volatile Object[] items;
    private AtomicInteger head;
    private AtomicInteger tail;
    private int capacity;

    public LoopArrayQueue(int capacity) {
        this.capacity = capacity;
        this.items = new Object[capacity];
        this.head = new AtomicInteger(0);
        this.tail = new AtomicInteger(0);
    }

    public boolean enqueue(Object item){
        int preTail = 0;
        int newTail = 0;

        for (;;){
            preTail = tail.get();//①获得此刻的tail值
            newTail = (preTail+1)%capacity;
            if (newTail == head.get()) return false;
            if (tail.compareAndSet(preTail,newTail)){//验证此刻主内存的tail值是否为①出所获得的值 如果是将新值赋予tail
                items[preTail] = item;
                return true;
            }
        }
    }

    public Object dequeue(){
        int preHead = 0;
        int newHead = 0;

        for (;;){
            preHead = head.get();
            if (preHead == tail.get()) return null;
            newHead = (preHead + 1)%capacity;
            if (head.compareAndSet(preHead,newHead)){
                return items[preHead];
            }
        }
    }
    
}

联想

与之相对的synchronized与wait和notify结合使用,且wait在while循环中!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值