JUC(二)JAVA线程池开启,等待全部执行完毕,配合计数器使用,List并发异常解决

简介: JUC(二)JAVA线程池开启,等待全部执行完毕,配合计数器使用,List并发异常解决

前言

  • 日常写代码过程中,我经常会有一些处理很多数据的业务,如一些定时任务,需要用到线程池

1.定义一个线程池

ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),//这里我获取的物理机的核心线程数
                2,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

1.1线程池七大参数

  1. corePoolSize 核心线程数目 - 池中会保留的最多线程数
  2. maximumPoolSize 最大线程数目 - 核心线程+救急线程的最大数目
  3. keepAliveTime 生存时间 - 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
  4. unit 时间单位 - 救急线程的生存时间单位,如秒、毫秒等
  5. workQueue - 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
  6. threadFactory 线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
  7. handler 拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略

1 抛异常 java.util.concurrent.ThreadPoolExecutor.AbortPolicy
2 由调用者执行任务 java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
3 丢弃任务 java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
4 丢弃最早排队任务 java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy

1.2使用线程池(1.配合CompletableFuture.supplyAsync()使用)

List<Integer> list = new CopyOnWriteArrayList();

Data data = new Data();

for (int i = 0; i < 3000; i++) {

    CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
        try {
            System.out.println(Thread.currentThread().getName());
            return data.getData();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1;
    },poolExecutor);
    try {
        list.add(supplyAsync.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}
poolExecutor.shutdown();

1.2.1 CopyOnWriteArrayList

  • CopyOnWriteArrayList类最大的特点就是,在对其实例进行修改操作(add/remove等)会新建一个数据并修改,修改完毕之后,再将原来的引用指向新的数组。这样,修改过程没有修改原来的数组。也就没有了ConcurrentModificationException错误。
  • 在这里插入图片描述

1.3使用线程池(2.配合CountDownLatch()使用)

List<String> list = new ArrayList<>();
list.add("test1");
list.add("test2");
list.add("test3");
list.add("test4");
list.add("test5");
CountDownLatch countDownLatch = new CountDownLatch(list.size());

ExecutorService threadPool = new ThreadPoolExecutor(
        2,
        Runtime.getRuntime().availableProcessors(),
        3,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(3),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.CallerRunsPolicy());//四种拒绝策略

try {
    for (String s : list) {
        threadPool.execute(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + "   --------------ok");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown(); //-1
            }
        });
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    countDownLatch.await();
    //线程池用完 程序结束, 关闭线程池
    threadPool.shutdown();
}
  • countDownLatch.await(); 等待计数器归零

总结

  • CPU密集型任务(N+1):这种任务消耗的主要是CPU资源,可以将线程数设置为N(CPU核心数)+1,比CPU核心数多出来一个线程是为了防止线程偶发的缺页中断,或者其他原因导致的任务暂停而带来的影响。一旦任务停止,CPU就会处于空闲状态,而这种情况下多出来一个线程就可以充分利用CPU的空闲时间
  • I/O密集型(2N):这种任务应用起来,系统大部分时间用来处理I/O交互,而线程在处理I/O的是时间段内不会占用CPU来处理,这时就可以将CPU交出给其他线程使用。因此在I/O密集型任务的应用中,可以配置多一些线程,具体计算方是2N。
相关文章
|
14天前
|
安全 Java 调度
解锁Java并发编程高阶技能:深入剖析无锁CAS机制、揭秘魔法类Unsafe、精通原子包Atomic,打造高效并发应用
【8月更文挑战第4天】在Java并发编程中,无锁编程以高性能和低延迟应对高并发挑战。核心在于无锁CAS(Compare-And-Swap)机制,它基于硬件支持,确保原子性更新;Unsafe类提供底层内存操作,实现CAS;原子包java.util.concurrent.atomic封装了CAS操作,简化并发编程。通过`AtomicInteger`示例,展现了线程安全的自增操作,突显了这些技术在构建高效并发程序中的关键作用。
41 1
|
14天前
|
Java 开发者
解锁并发编程新姿势!深度揭秘AQS独占锁&ReentrantLock重入锁奥秘,Condition条件变量让你玩转线程协作,秒变并发大神!
【8月更文挑战第4天】AQS是Java并发编程的核心框架,为锁和同步器提供基础结构。ReentrantLock基于AQS实现可重入互斥锁,比`synchronized`更灵活,支持可中断锁获取及超时控制。通过维护计数器实现锁的重入性。Condition接口允许ReentrantLock创建多个条件变量,支持细粒度线程协作,超越了传统`wait`/`notify`机制,助力开发者构建高效可靠的并发应用。
34 0
|
3天前
|
算法 Java
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
该博客文章综合介绍了Java并发编程的基础知识,包括线程与进程的区别、并发与并行的概念、线程的生命周期状态、`sleep`与`wait`方法的差异、`Lock`接口及其实现类与`synchronized`关键字的对比,以及生产者和消费者问题的解决方案和使用`Condition`对象替代`synchronized`关键字的方法。
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
|
10天前
|
网络协议 Java 编译器
Java常见异常及对应解决办法
Java常见异常及对应解决办法
28 10
|
3天前
|
存储 Java 程序员
|
9天前
|
Java 编译器 程序员
Java面试题-异常
Java面试题-异常
25 6
|
10天前
|
网络协议 Java 数据库连接
13 Java异常(异常过程解析、throw、throws、try-catch关键字)
13 Java异常(异常过程解析、throw、throws、try-catch关键字)
30 2
|
15天前
|
存储 Java 编译器
Java内存区域与内存溢出异常 - 运行时数据区
【8月更文挑战第2天】Java运行时数据区包括:1) 程序计数器:记录线程执行字节码的行号,线程私有;2) Java虚拟机栈:描述方法执行的内存模型,线程私有,深度过大抛出`StackOverflowError`;3) 本地方法栈:服务于Native方法,线程私有;4) Java堆:所有线程共享,对象实例在此分配内存;5) 方法区:存储类信息、常量等数据;6) 运行时常量池:方法区的一部分,存放字面量和符号引用。不当使用如无限创建对象或过度递归调用会导致各种内存溢出错误。
|
18天前
|
Java 开发者
Java中的多线程与并发控制
【7月更文挑战第31天】在Java的世界中,多线程是提升程序性能和响应能力的关键。本文将通过实际案例,深入探讨Java多线程的创建、同步机制以及并发包的使用,旨在帮助读者理解并掌握如何在Java中高效地实现多线程编程。
35 3
|
5天前
|
存储 缓存 安全
聊一聊高效并发之线程安全
该文章主要探讨了高效并发中的线程安全问题,包括线程安全的定义、线程安全的类别划分以及实现线程安全的一些方法。