实际业务场景:
业务描述:
kafka监听开启线程执行后续业务逻辑
问题:
ArrayList 在迭代期间如果修改集合的内容,会抛出 ConcurrentModificationException 异常。
解决
CopyOnWriteArrayList 替换 ArrayList可以解决该问题,想知道原理的,建议上网查下CopyOnWriteArrayList 就行。
下面贴一部分我的代码
1.线程工具类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class ExecutorConfig {
@Bean("taskExecutor")
public Executor taskExecutro(){
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(50);
taskExecutor.setMaxPoolSize(100);
taskExecutor.setQueueCapacity(10000);
taskExecutor.setKeepAliveSeconds(60);
taskExecutor.setThreadNamePrefix("线程名前缀--");
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
return taskExecutor;
}
}
2.业务处理(部分)
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import java.util.concurrent.CopyOnWriteArrayList;
@Component
@Slf4j
public class xxxxConsumer {
//修改前
//private static final HashMap<String, List<Double>> hashListxxx = new HashMap<>();
private static final HashMap<String, CopyOnWriteArrayList<Double>> hashListxxx = new HashMap<>();
//监听程序
@KafkaListener(topics = "${spring.kafka.topic.data_tcp}", groupId = "group_data_tcp")
public void consumer(ConsumerRecord<String, String> record) {
taskExecutor.execute(() -> {
mydeal(record);
});
}
//kafka消费业务类
private void mydeal(ConsumerRecord<String, String> record){
//1.写场景
CopyOnWriteArrayList<Double> dddList = new CopyOnWriteArrayList<>();
dddList.add(我的业务值);
hashListxxx .put("key", dddList);
>>>....
//2.读场景,在这里我是计算方差时用流的方式读取
//计算方差
double avg = 这组数的平均值;
double sumPow = hashListxxx .get(key).stream().mapToDouble(v -> Math.pow((v - avg), 2)).sum();
double sPow = sumPow / hashListSigma.get(did).size();
}
}
- 正是因为多线程下可能迭代期间修改内容,所以产生上述ConcurrentModificationException异常
- 该异常出现原理在此不深究,大概为循环读操作是导致的迭代器某一数量差异,刚好此时另一线程执行写入操作,导致
3.CopyOnWriteArrayList源码
- 它继承了List的接口,所以业务中的操作方法不需要改变,使用很方便
- 要注意的是:它适用于读多少写的业务场景,并且修改有一定延迟,不能保证实时为最新的数据。牺牲性能换来的是多线程下的安全
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
}