并发实战----ABA的处理方法:AtomicStampedReference用法

       hello大家好,我是小卡,昨天给大家浅谈了高并发下的CAS算法,以及更深层次的compareAndSwapObejct方法。在文章的最后提出了一个问题就是如何解决ABA的问题,今天花一点时间把这个问题给他家讲一下。

     

       首先我们再来回顾一波为什么会出现ABA?

       在多cpu的服务器中可能会出现多线程操作这个容器,并同时执行CAS,因为cpu之前的任务调度排序不同,执行的速度也可能会不同,就可能会出现A还在执行compare方法的时候,B线程已经执行完swap操作,同时将内存值修改成了A线程的预期值,这时候计算机以为操作是对的。但是这确实有个错误的操作,这就是数据版本不一致的问题。
   

         昨天我们也提到过使用AtomicStampReference来解决和这个问题,今天我们就来看看什么是AtomicStampReference。

public class AtomicStampedReference<V> {

    // 内部类,atomicStampedReference的本质就只这个类
    private static class Pair<T> {
        // 对象引用
        final T reference;
        // 版本号
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

    private volatile Pair<V> pair;

        我们再来看一看他的核心方法:

public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    Pair<V> current = pair;
    //  标蓝方法就是解决ABA问题的关键算法
    return
        // 原始值是否等于当前值 原始值 = 线程进入时的node值, 当前值 = 有可能被其他线程操作后的值
        expectedReference == current.reference &&
        // 原始版本是否和当前版本一致
        expectedStamp == current.stamp &&
        // 新值(替换值) = 原始值 && 新版本号 = 旧版本号 (想当于没有任何变化) 直接返回true了
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
        // 或者  新值、新版本号成功替换 原始值、原始版本号 返回操作成功!
         casPair(current, Pair.of(newReference, newStamp)));
}

        他的最底层也是用我们Usafe包下的compareAndSwapObject()方方法。

private boolean casPair(Pair<V> cmp, Pair<V> val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

上述代码就是AtomicStampedReference的核心方法,接下来我们就要学会如何去用,怎么样用才能够有效避免ABA。

 

package com.cdzg.shop.saleticket;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @author xiefei
 * @version 1.0
 * @date 2020/5/14 14:40
 */
public class Test {
    public static void main(String[] args) {
        /**
         *  这里需要重点注意的是因为我们解决ABA问题重点是关注 数据
         *  所以我们关注的点是在数据,而不是ConcurrentHashMap这个容器
         *  所以我们只需要在将关注的数据用AtomicStampedReference修饰就好了
         *  我第一次使用AtomicStampedReference便出现过这个问题
         *  希望大家引以为戒!!!!
         */
        ConcurrentHashMap<String,AtomicStampedReference<String>> concurrentHashMap = new ConcurrentHashMap<>();
        AtomicStampedReference<String> stamp = new AtomicStampedReference<>("person",0);
        String mapKey = "original";
        concurrentHashMap.put(mapKey,stamp);

        // 起线程模拟高并发操作同一个node
        Thread mainThread = new Thread(() ->{
            System.out.println("----进入主线程----");
            // 原始数据
            AtomicStampedReference<String> reference = concurrentHashMap.get(mapKey);
            String oldStr = reference.getReference();
            int oldValue = reference.getStamp();
            // 模拟主线程网络比较慢
            try{
                Thread.sleep(2000);
            }catch (InterruptedException e){
                System.out.println(e);
            }
            AtomicStampedReference<String> concurrentRef = concurrentHashMap.get(mapKey);
            System.out.println("当前数据:" + concurrentRef.getReference() + "--- 当前版本: " + concurrentRef.getStamp() );
            // 操作数据
            boolean mainFlag = reference.compareAndSet(oldStr, "god", oldValue, oldValue + 10);
            if(mainFlag) concurrentHashMap.put(mapKey,reference);
            System.out.println("----主线程操作结果----:" + mainFlag);
            System.out.println("----主线程结束----");
        },"主线程");

        Thread disturbThread = new Thread(() ->{
            System.out.println("----进入干扰线程----");
            AtomicStampedReference<String> reference = concurrentHashMap.get(mapKey);
            String oldStr = reference.getReference();
            int oldValue = reference.getStamp();
            // 操作数据
            boolean disturbFlag = reference.compareAndSet(oldStr, "devil", oldValue, oldValue + 10);
            concurrentHashMap.put(mapKey,reference);
            System.out.println("----干扰线程操作结果----:" + disturbFlag);
            System.out.println("----干扰线程结束----");
        },"干扰线程");

        mainThread.start();
        disturbThread.start();
    }
}

          提出操作结果,我们成功的利用AtomicStampedReference避免了ABA问题!

    

         注:这里有一点需要提醒大家,面试官可能会在面试的时候问到如何解决ABA的问题,我们在回答的时候要注意这个版本问题。首先这个版本使我们自定义的,其次这个版本的增幅也是我们自定义的 ,如上述代码。因为我看见有很博客写的是内置版本,自动版本+1就很气,那是错的,不可取的!

        希望大家一键三连!!!爱你们!!!

        并发编程CAS链接:https://blog.csdn.net/qq_39941165/article/details/106092241

        

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以帮你解决这个问题。这是一个简单的上下文无关文法,我们可以使用传统的消除无用符号的方法解决它。 以下是实现的步骤: 1. 找到所有可以到达终结符号的非终结符号。 2. 找到所有可以推导出至少一个终结符号的非终结符号。 3. 从初始符号开始,找到所有可以推导出的非终结符号。 4. 删除所有不在这些集合中的非终结符号和产生式。 下面是使用 Python 语言实现的代码: ```python # 输入文法 grammar = { 'S': ['A', 'B'], 'A': ['a', 'C'], 'C': ['b', 'A', 'b', 'a'] } # 找到所有可以到达终结符号的非终结符号 def find_reachable_symbols(grammar): reachable = set() stack = ['S'] while stack: symbol = stack.pop() if symbol in reachable: continue reachable.add(symbol) for production in grammar.get(symbol, []): for char in production: if char.isupper(): stack.append(char) return reachable # 找到所有可以推导出至少一个终结符号的非终结符号 def find_generating_symbols(grammar): generating = set() while True: old_len = len(generating) for symbol, productions in grammar.items(): if symbol in generating: continue for production in productions: if all(char in generating or not char.isupper() for char in production): generating.add(symbol) break if len(generating) == old_len: break return generating # 找到所有可以推导出的非终结符号 def find_derivable_symbols(grammar, start_symbol): derivable = set() stack = [start_symbol] while stack: symbol = stack.pop() if symbol in derivable: continue derivable.add(symbol) for production in grammar.get(symbol, []): for char in production: if char.isupper(): stack.append(char) return derivable # 删除无用符号和产生式 def remove_useless_symbols(grammar): reachable = find_reachable_symbols(grammar) generating = find_generating_symbols(grammar) derivable = find_derivable_symbols(grammar, 'S') new_grammar = {} for symbol, productions in grammar.items(): if symbol in reachable and symbol in generating and symbol in derivable: new_productions = [] for production in productions: if all(char in reachable or not char.isupper() for char in production): new_productions.append(production) if new_productions: new_grammar[symbol] = new_productions return new_grammar # 测试代码 grammar = { 'S': ['A', 'B'], 'A': ['a', 'C'], 'C': ['b', 'A', 'b', 'a'] } new_grammar = remove_useless_symbols(grammar) print(new_grammar) # {'S': ['A'], 'A': ['a'], 'C': ['b', 'A', 'b', 'a']} ``` 使用上面的代码,输入的文法S-AB|a|BB,A-a,C-b|Aba就会被转换成S-a的形式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值