并发编程工具 - juc的atomic工具类、AtomicInteger的项目使用

目录

atomic包的实现原理

atomic包的工具分类

AtomicInteger分析和项目使用


    juc下面的atomic包下面所以的工具类都没有使用AQS,但是却是线程安全的(基于volatile + CAS)。由于没有使用AQS双向队列(底层没有使用LockSupport的park和unpark),则并没有互斥锁,所以可以理解成线程安全的无锁工具类。

atomic包的实现原理

    1)、volatile关键字基于JVM屏蔽底层的内存屏障差异,但都解决了有序性(防止指令重排序)的问题。
    2)、并且Jdk5之后,对volatile关键字进行了增强(volatile修饰的变量 写Hanppen-Before于读取),结合Happen-Before的顺序性和传递性实现了可见性问题。具体可参见:并发理论基础 - JMM模型和Happens-Before规则
    3)、基于无锁的CAS(乐观锁思想),底层会调用Unsafe的相关方法实现了原子性。

至于CAS的ABA问题可以参见:并发编程基础 - synchronized和volatile并发模型对比、从顶层俯瞰juc##四、CAS存在的问题和解决方案

 

atomic包的工具分类

1)、基本数据类型

    AtomicBoolean、AtomicInteger、AtomicLong

2)、数组

    AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

3)、叠加器(没有使用CAS并性能高于基本数据类型)

    DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder

4)、引用类型

    AtomicReference、AtomicStampedReference【使用版本号解决ABA问题】、AtomicMarkableReference【使用Boolean解决ABA问题

5)、对象属性更新器

    AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater

 

AtomicInteger分析和项目使用

    

    AtomicInteger的源码结构和方法,直接调用了Unsafe的CAS相关方法:

public class AtomicInteger extends Number implements java.io.Serializable {

    // Unsafe的compareAndSwapInt解决原子性
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // volatile解决可见和有序性
    private volatile int value;
    private static final long valueOffset;

    /**
     * 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);
    }

    static {
        try {
            valueOffset = unsafe.objectFieldOffset(java.util.concurrent.atomic.AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }
}

个人理解,只要是并发的处理Integer等基本数据类型时,就可以使用atomic相关的类,那么在其他优秀的框架中是怎么使用AtomicInteger的呢?

比如tomcat线程池是基于ThreadPoolExecutor进行扩展的,在ThreadPoolExecutorImpl类中就基于AtomicInteger统计了执行的总任务数,基于AtomicLong统计了当前线程最后一次被终止的时间,具体源码如下:

public class ThreadPoolExecutorImpl extends java.util.concurrent.ThreadPoolExecutor {
    /**
     * The number of tasks submitted but not yet finished. This includes tasks
     * in the queue and tasks that have been handed to a worker thread but the
     * latter did not start executing the task yet.
     * This number is always greater or equal to {@link #getActiveCount()}.
     */
    private final AtomicInteger submittedCount = new AtomicInteger(0);
    private final AtomicLong lastContextStoppedTime = new AtomicLong(0L);

    /**
     * Most recent time in ms when a thread decided to kill itself to avoid
     * potential memory leaks. Useful to throttle the rate of renewals of
     * threads.
     */
    private final AtomicLong lastTimeThreadKilledItself = new AtomicLong(0L);
}

最近自己在写一个报表导出项目(微服务中的一个),导出项目的真正执行的项目(不是微服务,单独部署的Spring boot项目)进行了解耦【为了防止因为导出的异常将整个系统拖垮】。封装的底层使用Http Client线程池,使用Spring Boot的yml(或者properties)中配置的服务列表进行负载均衡调用。调用时可以指定具体的负载均衡策略,其中轮询是少不了的。这时我就使用了 AtomicInteger【ATOMIC_ADDER属性】,如下:

/**
 *  调用 报表执行服务的 服务均衡工具类
 * @author kevin
 * @date 2020/10/23 11:13
 * @since 1.0.0
 */
@Slf4j
@Configuration
@AutoConfigureAfter(ReportExecuteConfigurationProperties.class)
public class ReportExecuteLbs implements ApplicationContextAware, InitializingBean {

    public static ApplicationContext applicationContext;

    public static LbsStrategyEnum strategy = RANDOM;

    /**
     * 多线程使用同一叠加计数器, volatile + CAS处理
     */
    private static final AtomicInteger ATOMIC_ADDER = new AtomicInteger(0);

    /**
     * 组装好请求的uri 列表,允许修改,特别是后续使用 Zookeeper/Nacos等带钩子的配置中心
     */
    private final static CopyOnWriteArrayList<String> uriList = new CopyOnWriteArrayList<>();

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        ReportExecuteLbs.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
        ReportExecuteConfigurationProperties properties = applicationContext.getBean(ReportExecuteConfigurationProperties.class);
        List<String> list = properties.getList();
        if (list.isEmpty()) {
//            log.error("获取到的 报表导出执行服务列表为空 !");
            throw new RuntimeException("获取到的 报表导出执行服务列表为空 !");
        }
        if (StringUtil.isBlank(properties.getLoadBalancingStrategy())) {
//            log.info("配置的执行策略为空,使用默认的 随机策略 !");
            strategy = RANDOM;
        } else {
            String loadBalancingStrategy = properties.getLoadBalancingStrategy();
            strategy = LbsStrategyEnum.strategyValue(loadBalancingStrategy);
        }
        list.forEach(info -> uriList.addIfAbsent(properties.getHttpScheme() + info));
    }

    /**
     * 根据配置的策略获取请求的url
     * @return url地址
     */
    public static String getHttp() {
        return getHttp(strategy);
    }

    /**
     *  根据配置的策略获取请求的url
     * @param lbsStrategy 策略字符串
     * @return url地址
     */
    public static String getHttp(String lbsStrategy) {
        LbsStrategyEnum lbsStrategyEnum = LbsStrategyEnum.strategyValue(lbsStrategy);
        if (lbsStrategyEnum == null) {
            return getHttp();
        }
        return getHttp(lbsStrategyEnum);
    }

    /**
     * 根据配置的策略获取请求的url
     * @return url地址
     */
    public static String getHttp(LbsStrategyEnum lbsStrategyEnum) {
        if (uriList.size() == 1) {
            return uriList.get(0);
        }
        if (lbsStrategyEnum == RANDOM) {
            int index = ThreadLocalRandom.current().nextInt(uriList.size());
            return uriList.get(index - 1);
        } else {
            return uriList.get(ATOMIC_ADDER.getAndIncrement() % uriList.size());
        }
    }

}

 

 

 

 

已标记关键词 清除标记
本人最近写了一段代码,用到了AtomicInteger对这个变量,但是期待的结果与代码的实际运行 结果相差好大,现将代码奉上,求各位大牛给下解决方案。 class IDGeneration { private final static ConcurrentHashMap<Long, AtomicInteger> idMap = new ConcurrentHashMap<Long, AtomicInteger>(); private static final long baseTime = 1462377600000L; private Lock lock; IDGeneration() { lock = new ReentrantLock(); } @SuppressWarnings("unused") public Long generateRelativeIncrementUniqueId(String flag) { String dateString = GengratedUnqueId.formatDate(new Date(), GengratedUnqueId.PATTERN); Long currentTime = (System.currentTimeMillis() - baseTime); AtomicInteger queueId = new AtomicInteger(0); int i = 0; Long tempValue = null; lock.lock(); try { queueId = idMap.get(currentTime); if (queueId == null) { queueId = new AtomicInteger(0); idMap.clear(); idMap.put(currentTime, queueId); tempValue = (currentTime << 5 | queueId.get()); } else { queueId.getAndIncrement(); idMap.put(currentTime, queueId); tempValue = (currentTime << 5 | queueId.get()); } } finally { lock.unlock(); } return tempValue; return null; } } 这是测试代码: public static void main(String[] args) throws Exception { static ConcurrentHashMap<Long, String> map = new ConcurrentHashMap<Long, String>(); final IDGeneration idGeneration = new IDGeneration(); for (int i = 0; i < 100; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { long value = idGeneration .generateRelativeIncrementUniqueId(); map.put(value, ""); } }); thread.start(); } TimeUnit.SECONDS.sleep(5); System.out.println("map.size===" + map.size()); }
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页