1、ListUtils工具类(提供将List均分成n个list和将List分成每份count数量的List操作)
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ListUtils {
/**
* 将一个list均分成n个list:
* @param source
* @param n
* @return
*/
public static <T> List<List<T>> avgList(List<T> source, int n) {
List<List<T>> result = new ArrayList<>();
//(先计算出余数)
int remainder = source.size() % n;
//然后是商
int number = source.size() / n;
//偏移量
int offset = 0;
for (int i = 0; i < n; i++) {
List<T> value;
if (remainder > 0) {
value = source.subList(i * number + offset, (i + 1) * number + offset + 1);
remainder--;
offset++;
} else {
value = source.subList(i * number + offset, (i + 1) * number + offset);
}
result.add(value);
}
return result;
}
/**
* 将list分成每份count的数量的list:
* @param resList
* @param count
* @return
*/
public static <T> List<List<T>> splitList(List<T> resList, int count) {
if (resList == null || count < 1) {
return null;
}
//long t1 = System.currentTimeMillis();
List<List<T>> result = new ArrayList<List<T>>();
int size = resList.size();
if (size <= count) {
// 数据量不足count指定的大小
result.add(resList);
} else {
int pre = size / count;
int last = size % count;
// 前面pre个集合,每个大小都是count个元素
for (int i = 0; i < pre; i++) {
List<T> itemList = new ArrayList<T>();
for (int j = 0; j < count; j++) {
itemList.add(resList.get(i * count + j));
}
result.add(itemList);
}
// last的进行处理
if (last > 0) {
List<T> itemList = new ArrayList<T>();
for (int i = 0; i < last; i++) {
itemList.add(resList.get(pre * count + i));
}
result.add(itemList);
}
}
/*long t2 = System.currentTimeMillis();
System.out.println("splitList====>>> resList.size:" + resList.size()
+ ", count:" +count + ", costs time:" + (t2 - t1) + " ms");*/
return result;
}
public static <T> List<List<T>> splitList2(List<T> list, int count) { //效率低不推荐使用
long t1 = System.currentTimeMillis();
int limit = (list.size() + count - 1) / count;
// 方法一:使用流遍历操作
/*List<List<T>> result = new ArrayList<>();
Stream.iterate(0, n -> n + 1).limit(limit).forEach(i -> {
result.add(list.stream().skip(i * count).limit(count).collect(Collectors.toList()));
});*/
//方法二:获取分割后的集合
List<List<T>> result = Stream.iterate(0, n -> n + 1).limit(limit).parallel().map(a -> list.stream().skip(a * count).limit(count).parallel().collect(Collectors.toList())).collect(Collectors.toList());
long t2 = System.currentTimeMillis();
System.out.println("splitList====>>> resList.size:" + list.size()
+ ", count:" +count + ", costs time:" + (t2 - t1) + " ms");
return result;
}
public static void main(String[] args) {
/*List<Integer> list=new ArrayList<>();
for (int i = 0; i < 17; i++) {
list.add(i);
}
List<List<Integer>> avgList = avgList(list, 5);
System.out.println("avgList: " + avgList);
List<List<Integer>> splitList = splitList(list, 5);
System.out.println("splitList: " + splitList);
ArrayList<Integer> arr_list = new ArrayList<>();
for (int i = 0; i < splitList.size(); i++) {
List<Integer> subList = splitList.get(i);
arr_list.addAll(subList);
}
System.out.println("arr_list: " + arr_list);*/
}
}
2、模拟一千万请求数据,创建一个线程池10线程个控制最大并发数,每个线程一次处理10万条,开启100个线程。(用于控制每个批次线程的数据量业务场景)
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.test.utils.ListUtils;
public class ConcurrencyTest {
public static void main(String[] args) {
int count = 10000000;
List<Long> listKey = new ArrayList<>();
for (int i = 0; i < count; i++) {
listKey.add(114560315500000000L + i);
}
ConcurrencyTest concurrencyTest = new ConcurrencyTest();
List<Long> valueList1 = concurrencyTest.getValueList1(listKey);
System.out.println("====>> getValueList1 valueList.size: " + valueList1.size());
}
private final static Logger log = LoggerFactory.getLogger(ConcurrencyTest.class);
/**
* 模拟一千万请求数据,创建一个线程池10个线程控制最大并发数,每个线程一次处理10万条,开启100个线程。(用于控制每批次线程的数据量业务场景)
* @param listKey 请求处理的总数据量
* @return
*/
public List<Long> getValueList1(List<Long> listKey) {
/**
(1)newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
*/
//创建一个线程池
final ExecutorService executorService = Executors.newFixedThreadPool(10); //用于控制同时并发执行的线程数,使用线程池创建资源提高效率
List<Long> list_val = Collections.synchronizedList(new ArrayList<>()); //保证多线程操作的是同一个List
try {
long t1 = System.currentTimeMillis();
int max_one_batch = 100000; // 一批次最多处理100000条数据
List<List<Long>> newList = ListUtils.splitList(listKey, max_one_batch);
int runSize = newList.size(); // 开启的线程数
/**
* CountDownLanch 只需要在子线程执行之前, 赋予初始化countDownLanch, 并赋予线程数量为初始值。
* 每个线程执行完毕的时候, 就countDown一下。主线程只需要调用await方法, 可以等待所有子线程执行结束。
*/
final CountDownLatch countDownLatch = new CountDownLatch(runSize); //计数器
/**
* Semaphore(信号量)是用来控制同时访问特定资源的线程数量,拿到信号量的线程可以进入,否则就等待。
* 通过acquire()和release()获取和释放访问许可。
*/
final Semaphore semaphore = new Semaphore(runSize); //信号量
// 循环创建线程
for (int j = 0; j < runSize; j++) {
final int i = j;
executorService.execute(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
// 执行程序
List<Long> subList = newList.get(i);
List<Long> sub_ret = getValue(subList);
list_val.addAll(sub_ret);
log.info(Thread.currentThread().getName() + ": 当前线程/总线程: [" + i + "/" + runSize + "]"
+ ", 处理的数据条数:" + subList.size());
semaphore.release();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
// 计数器减一
countDownLatch.countDown();
}
}
});
}
// 阻塞主线程等待所有线程执行完成