目录
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());
}
}
}