Java 异步编程——常用线程池 ThreadPoolExecutor

【JAVA 多线程】线程池 ThreadPoolExecutor使用详解 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,时间单位,任务队列,创建线程工厂,任务的拒绝策略);线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作 线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这 个工作队列里。如果工作队列满了,则进入下个流程。 阅读详情

ThreadPoolExecutor

java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类。

线程池底层示例图:

ThreadPoolExecutor 的构造方法:

private volatile int corePoolSize;
private volatile int maximumPoolSize;
private volatile long keepAliveTime;
private volatile boolean allowCoreThreadTimeOut;
private volatile int maximumPoolSize;
private final BlockingQueue<Runnable> workQueue;
private volatile RejectedExecutionHandler handler;
private volatile ThreadFactory threadFactory;

private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

核心参数详解

  1. corePoolSize:线程池中的常驻核心线程数。核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了 prestartAllCoreThreads() 或者 prestartCoreThread() 方法,这2个方法是预创建线程的意思,即在没有任务到来之前就创建 corePoolSize 个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到 corePoolSize 后,就会把到达的任务放到缓存队列当中;

  2. maximumPoolSize:线程池最大线程数(非核心线程数),表示在线程池中最多能创建多少个线程;

  3. keepAliveTime:多余的空闲线程的存活时间。表示线程没有任务执行时最多保持多久时间会终止。当前线程池数量超过corePoolSize时,当空闲的时间达到keepAliveTime值时,多余的空闲线程会被直接销毁直到只剩下corePoolSize个线程为止。

    默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于 corePoolSize 时,如果一个线程空闲的时间达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean) 方法,在线程池中的线程数不大于 corePoolSize 时,keepAliveTime 参数也会起作用,直到线程池中的线程数为0;

  4. TimeUnit:时间单位,keepAliveTime 的单位。

  5. ThreadFactory:线程工厂,主要用来创建线程,用于创建线程一般用默认的即可。

  6. workQueue:一个阻塞队列,用来存储等待执行的任务(被提交但未被执行的任务),维护着等待执行的 Runnable 对象。

  7. RejectedExecutionHandler:饱和策略,线程拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数。

线程池的处理流程

  1. 在创建了线程池后,开始等待请求。
  2. 当调用 execute() 方法添加一个请求任务时,线程池会做出如下判断:
    1. 当任务数量少于 corePoolSize 时,会自动创建 thread 来处理这些任务并加入线程池;
    2. 当添加任务数大于 corePoolSize 且少于 maximumPoolSize 时,不再创建线程,而是将这些任务放到阻塞队列中,等待被执行;
    3. 当阻塞队列满了之后,线程池中的线程数没有超过 maximumPoolSize 最大线程数,则新建一个线程处理任务并加入到线程池中,从而加速处理阻塞队列;
    4. 当阻塞队列满了之后,且添加任务大于 maximmPoolSize 时,根据饱和策略决定是否容许继续向线程池中添加任务,默认的饱和策略是 AbortPolicy(直接丢弃)。
  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  4. 空闲的线程(非核心线程)会在到达 keepAliveTime 时间之后没有被使用的话就被回收。所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。

提交优先级:任务先提交到核心线程,然后提交到队列中,最后提交到非核心线程。

执行优先级:先执行核心线程中任务,然后执行非核心线程中任务,最后执行队列中的任务。

线程池中使用的阻塞队列

在前面我们多次提到了任务缓存队列,即 workQueue,它用来存放等待执行的任务。

workQueue 的类型为 BlockingQueue<Runnable>,通常可以取下面三种类型:

  1. ArrayBlockingQueue:基于数组的先进先出有界阻塞队列,此队列创建时必须指定大小;
  2. LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE(65536),当大量请求任务时,容易造成内存耗尽。
  3. SynchronousQueue:是一个特殊的 BlockingQueue,它没有容量。这个队列只有在另一个线程在同步 remove 的时候才可以 put 成功,对应到线程池中,简单来说就是如果有线程池任务处理完了,调用 poll 或者 take 方法获取新的任务的时候,新提交的任务才会 put 成功,否则如果当前的线程都在忙着处理任务,那么就会 put 失败,也就会走扩容的逻辑(新建一个线程来执行新来的任务)。

阻塞队列(BlockingQueue)

  • BlockingQueue 继承了 Queue 接口,是队列的一种。Queue 和 BlockingQueue 都是在 Java 5 中加入的。在新增的 Concurrent 包中(java.util.concurrent.BlockingQueue)。

    public interface BlockingQueue<E> extends Queue<E> {...}
    
  • 为什么出现 BlockingQueue

    BlockingQueue 很好的解决了多线程高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。

    多线程环境中,通过队列可以很容易实现数据共享,比如经典的“生产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的情况呢?理想情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。然而,在 concurrent 包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

    作为 BlockingQueue 的使用者,我们再也不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切 BlockingQueue 都给你一手包办了。(在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒)。

  • 阻塞队列的特点

    阻塞队列与普通队列的区别在于:

    • 当队列是空的时,从队列中获取元素的操作将会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。
    • 当队列是满时,往队列里添加元素的操作会被阻塞。试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列。
  • 阻塞队列提供了四种处理方法:

    方法\处理方式抛出异常返回特殊值一直阻塞超时退出
    插入方法add(e)offer(e)put(e)offer(e,time,unit)
    移除方法remove()poll()take()poll(time,unit)
    检查方法element()peek()不可用不可用
    • 异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出 IllegalStateException(“Queue full”) 异常。当队列为空时,从队列里获取元素时会抛出 NoSuchElementException 异常 。

    • 返回特殊值:插入方法会返回是否成功,成功则返回 true。移除方法,则是从队列里拿出一个元素,如果没有则返回 null。

    • 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
    • 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。
  • 阻塞队列的成员

    队列有界性锁数据结构
    ArrayBlockingQueuebounded(有界)加锁arrayList
    LinkedBlockingQueueoptionally-bounded加锁linkedList
    PriorityBlockingQueueunbounded加锁heap
    DelayQueueunbounded加锁heap
    SynchronousQueuebounded加锁无
    LinkedTransferQueueunbounded加锁heap
    LinkedBlockingDequeunbounded无锁heap
    • ArrayBlockingQueue:是一个用数组实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。支持公平锁和非公平锁。【注:每一个线程在获取锁的时候可能都会排队等待,如果在等待时间上,先获取锁的线程的请求一定先被满足,那么这个锁就是公平的。反之,这个锁就是不公平的。公平的获取锁,也就是当前等待时间最长的线程先获取锁】

      • ArrayBlockingQueue 在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。Doug Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。而在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。

        数组实现、有界、先进先出、队列不支持空元素、线程安全的阻塞循环队列;

        对数据操作时,共用一把锁,所以不能同时读写操作;

        enqueue()和dequeue()方法是入队和出队的核心方法,他们分别通知”队列非空”和”队列非满”,从而使阻塞中的入队和出队方法能够继续执行,以实现生产者消费者模式。

    • LinkedBlockingQueue:一个由链表结构组成的有界队列,此队列的长度为 Integer.MAX_VALUE。

      • 此队列按照先进先出的顺序进行排序。

        链表实现、先进先出、队列不支持空元素、线程安全的阻塞队列。

        内部有两把锁,插入和取出各一把,互不干扰,所以能同时进行读写操作。

        由于 FixedThreadPool 和 SingleThreadExecutor 的线程数是固定的,所以一般用容量无穷大的 LinkedBlockingQueue 队列来存任务。

    • PriorityBlockingQueue: 一个支持线程优先级排序的无界队列,默认自然序进行排序,也可以自定义实现compareTo()方法来指定元素排序规则,不能保证同优先级元素的顺序。

      • PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁。

        数组实现、通过排序指定出对规则、不允许为null的元素插入、线程安全的无界队列。

        可以指定内部元素的排序规则,依赖Comparator来确保不同元素的排序位置。

        添加到PriorityBlockingQueue队列中的元素对应的Java类,通常需要实现Comparable接口或者是可以默认排序的对象(如数字、字符串),否则会抛出 ClassCastException。

    • DelayQueue: 一个实现 PriorityBlockingQueue 实现延迟获取的无界队列,在创建元素时,可以指定多久才能从队列中获取当前元素。只有延时期满后才能从队列中获取元素。(DelayQueue可以运用在以下应用场景:

      1. 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。

      2. 定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。)

        可实现延时、用于放置实现了Delayed接口的对象、不允许为null的元素插入、线程安全的无界队列。

    • SynchronousQueue: 一个不存储元素的阻塞队列,每一个put操作必须等待take操作,否则不能添加元素。支持公平锁和非公平锁。SynchronousQueue的一个使用场景是在线程池里。Executors.newCachedThreadPool() 就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。

      • 一种无缓冲的等待队列,类似于无中介的直接交易,有点像原始社会中的生产者和消费者,生产者拿着产品去集市销售给产品的最终消费者,而消费者必须亲自去集市找到所要商品的直接生产者。

        容量为0,不存储任何元素,即没有数据缓存的空间。一对一,生产者和消费者缺一就阻塞。入队线程和出队线程必须一一匹配,否则任意先到达的线程会阻塞。比如ThreadA进行入队操作,在有其它线程执行出队操作之前,ThreadA会一直等待,反之亦然

        SynchronousQueue 提供两种实现方式,分别是 栈 和 队列 的方式实现。这两种实现方式中,栈 是属于非公平的策略,队列 是属于公平策略。默认情况下采用非公平性访问策略,当然也可以通过构造函数来设置为公平性访问策略(为true即可)。

        在java线程池newCachedThreadPool中就使用了这种阻塞队列。

    • LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列,相当于其它队列,LinkedTransferQueue队列多了transfer和tryTransfer方法。

    • LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。队列头部和尾部都可以添加和移除元素,多线程并发时,可以将锁的竞争最多降到一半。

4 种任务拒绝策略

当线程池的任务缓存队列已满并且线程池中的线程数目达到 maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

  1. ThreadPoolExecutor.AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常。
  2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,并将新任务加入对列,然后重新尝试执行任务(重复此过程)
  4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

自定义拒绝策略:实现 RejectedExecutionHandle 接口。

4 种不同的线程池

除了可以使用 ThreadPoolExecutor 自己根据实际情况创建线程池以外,Executor 框架也提供了线程池,可以通过工具类 Executors 来创建。

Executors 类提供了4种不同的线程池:newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor。(不过不建议使用 Executors 类来创建这些线程池,而是直接使用 ThreadPoolExecutor 自定义创建。)

  1. newCachedThreadPool 创建一个可缓存线程池(CachedThreadPool),如果线程池长度超过处理需要,可灵活回收空闲线程;若无可回收,则新建线程。(使用 Executors 创建该线程池会使 CPU 100%)。

    优点:如果当第二个任务开始,第一个任务已经执行结束,那么第二个任务会复用第一个任务创建的线程,并不会重新创建新的线程,提高了线程的复用率;否则可以创建线程加快任务处理。

    不足:这种方式虽然可以根据业务场景自动的扩展线程数来处理我们的业务,但是最多需要多少个线程同时处理是我们无法控制的。

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                    new SynchronousQueue<Runnable>());
    }
    
  2. newFixedThreadPool 创建一个固定大小的线程池(FixedThreadPool),可控制线程最大并发数,超出的线程会在队列中等待。(使用 Executors 创建该线程池会出现 OOM,因为使用 LinkedBlockingQueue)

    优点:newFixedThreadPool 的线程数是可以进行控制的,因此我们可以通过控制最大线程来使我们的服务器达到最大的使用率,同时又可以保证及时流量突然增大也不会占用服务器过多的资源。

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable());
    }
    
  3. newScheduledThreadPool 创建一个固定大小的线程池(ScheduledThreadPool),支持定时及周期性任务执行。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
  4. newSingleThreadExecutor 创建一个单线程化的线程池(SingleThreadExecutor),它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIF0,LIFO,优先级)执行。(使用 Executors 创建该线程池会出现 OOM,因为使用 LinkedBlockingQueue)

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>()));
    }
    

线程池的五种状态

线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated。

RUNNING

  • 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
  • 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。

SHUTDOWN

  • 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
  • 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

STOP

  • 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
  • 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN) -> STOP。

TIDYING

  • 状态说明:当所有的任务已终止,ctl 记录的”任务数量”为0,线程池会变为 TIDYING 状态。当线程池变为 TIDYING 状态时,会执行钩子函数 terminated()。terminated() 在 ThreadPoolExecutor 类中是空的,若用户想在线程池变为 TIDYING 时,进行相应的处理;可以通过重载 terminated() 函数来实现。
  • 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
  • 当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

TERMINATED

  • 状态说明:线程池彻底终止,就变成TERMINATED状态。
  • 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

Future 接口

Future 接口和实现 Future 接口的 FutureTask 类用来表示异步计算的结果。当我们把 Runnable 接口或 Callable 接口的实现类提交(submit)给 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 时,ThreadPoolExecutor 或 ScheduledThreadPoolExecutor 会向我们返回一个FutureTask对象。

Future 的类图结构

在这里插入图片描述

Future 接口一般用于表示异步计算的结果。

  • 异步工作机制,如果我们在主线程中直接写一个函数来执行任务,这是同步的任务,也就是说必须要等这个函数返回以后我们才能继续做接下的事情,但是如果这个函数返回的结果对接下来的任务并没有意义,那么我们等在这里是很浪费时间的,而 FutureTask 就提供了这么一个异步的返回结果的机制,当执行一个 FutureTask 的时候,我们可以接着做别的任务,在将来的某个时间,FutureTask 任务完成后会返回 FutureTask 对象来包装返回的结果,只要调用这个对象的 get() 方法即可获取返回值。

Future 接口主要包括5个方法。

  1. get() 方法:用于获取执行完后的结果。get() 是一个阻塞操作,即调用 get(),任务结束获取返回的结果;如果调用时工作还没有结束,则会阻塞线程,直到任务执行完毕。
  2. get(long timeout, TimeUnit unit):在规定等待时间任务结束,获取结果;超时则会抛出一个TimeoutException。
  3. cancel(boolean mayInterruptIfRunning)方法:选择是否中断正在运行的task。如果我们 cancel 之后,再次调用 get() 方法,则会抛出 CancellationException。
  4. isDone() 方法:判断当前方法是否完成。
  5. isCancel() 方法:判断当前方法是否取消。
实例讲解ThreadPoolExecutor线程池任务执行过程 为了方便测试,设置核心线程数(corePoolSize)为2,最大线程数(maximumPoolSize)为3,任务队列长度为3。创建一个线程池,任务执行时长通过doSomeThing的sleep时长来决定。 问题1 启动线程池,还未提交任务,此时线程池有几个线程?答:0个,debug可以看出,创建线程池后,并没有往线程池添加线程。 提交第一个任务,待第一个任务执行完成之后,再提交第二个任务,此时线程池有几个线程?即在第一个任务执行完成后,提交第二个任务,是否会创建新线程?答:会创建新线程源码的注释上 阅读详情

相关推荐

Docker Desktop重装避坑:从卸载到安装的完整流程(Win10/Win11适用)

本文详细介绍了在Windows 10/11系统上彻底卸载和重新安装Docker Desktop的完整流程,包括深度清理残留文件、注册表项和虚拟磁盘,以及安装前的环境准备和安装后的验证步骤,帮助用户避免常见问题并优化Docker环境配置。

julia4scientist的博客 1107

java异步-线程、线程池

java异步-线程、线程池,Executors创建线程池创建线程的几种方式

睡竹的博客 2098

HC-05蓝牙模块详细配置、连接与使用过程

用手按着模块上的按键,再给蓝牙模块上电。当蓝牙模块上的灯慢闪的时候就说明进入了配置模式了。在配置蓝牙的时候一定要将串口助手按这个要求配置:设置波特率 38400,数据位 8 位,停止位 1 位,无校验位,这是固定操作! 根据蓝牙的主从模式进行不同的设置,二者主要区别就在于AT+ROLE这个指令如果这里没返回OK可能有一下几种情况:如果正常返回OK,那就可以进行下一步了 配置蓝牙密码的时候,特别是配置HC05,密码必须加双引号"",否则不能正常设置!!!! 给从机模式蓝牙配置好之后,将蓝牙重

qq_51688022的博客 2万+

java异步与线程池

Executors.创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待;Executors.创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程;Executors.创建单个线程数的线程池,它可以保证先进先出的执⾏顺序;Executors.创建⼀个可以执⾏延迟任务的线程池;是以period为间隔来执行任务的,如果任务执行时间小于period,则上次任务执行完成后会间隔period后再去执行下一次任务;

onePlus5T的博客 1941

谷歌(Google) Pixel 8/9 Pro(xl)如何获取 root 权限

谷歌(Google)Pixel8/9Pro(xl)等系列手机破解后,会在你的init_boot.img同路径生成一个magisk_patched.img,把这个文件拷贝到adb目录下,稍后还得把它刷回手机,这样手机才能获得Root权限,因为安卓14、15以后的内核结构调整,和Pixel7及以下的6、5、4、3、2等系列手机破解稍微不一致。因为ROOT权限的获取,需要使用ROM中的内核文件,个人强烈建议在官网下载ROM,直接刷最新版,其他地方的ROM如果没有校验码,请自行斟酌是否使用。

bugsycrack的博客 3489

三相直流无刷电机(带HALL传感器)BLDC控制一

本案例选择TIM0的CH0/CH1/CH2三个通道接入三路原始hall输出信号。三路TIM_CHx(48)通过配置存入到NIP寄存器(new input pattern,3 bits)内。未完待续。

m0_69929716的博客 406

java 线程池——异步任务

 为了解决这些问题,Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用。为我们在开发中处理线程的问题提供了非常大的帮助

guanshengg的博客 4078

DB2__基本概念——联邦

建立联邦 优点:优点是数据仍然保留在原来的存储位置,而不必建立一个集中的式数据仓储。  缺点:数据联邦查询反映会慢,不适合频繁查询,而且容易出现锁争用和资源冲突等问题 建立联邦表步骤 前提条件:知道两个数据库的ip,用户名,密码及被连数据库的版本 eg:  数据库  qqdb  ip: 22.5.225.225   端口:50000    密码:123456        数据库   ...

qq_38569524的博客 1257

CompletableFuture学习

我理解就是同时执行多个任务,加快响应速度。并且多线程能更好的发挥CPU的能力。并发:如果CPU是单核单线程,通过时间片轮询交替运行多个任务,像这种逻辑上多线程运行,实际上是一个线程在运行叫做并发;并行:如果CPU是双核双线程,那两个CPU同时执行任务,那就叫做并行; 线程池 对于新手来说,有现成的api那么我们介绍一下ThreadPoolExecutor看源码: 我们工作实际开发中经常用到CompletableFuture。那么他的作用是什么呢?作用:Api:三个一组三个一组三个一组三个一组一般项目中这样使

逾越的博客 1万+

ROS2 多机器人导航仿真入门教程(一)

RMF 是建立在 ROS 2 基础上的一系列可重复使用、可扩展的库和工具,可实现任何类型机器人系统的异构机群的互操作性。RMF 利用标准化的通信协议与部署机器人的基础设施、环境和自动化系统进行通信,以优化关键资源(如机器人、电梯、门、通道等)的使用。它通过资源分配和通过 RMF 核心(将在本书后面详细介绍)防止共享资源冲突,从而为系统增加智能。RMF 灵活而强大,几乎可以在任何通信层上运行,并与任何数量的物联网设备集成。RMF 的架构设计允许随着环境自动化水平的提高而扩展。

2506_90492529的博客 1659

IEC 61850标准协议解读 3.MMS服务

摘要:本文系统介绍了MMS(制造报文规范)的基础知识及其在IEC61850中的应用。首先解析MMS的核心定义、通信流程(带确认/不带确认服务)、虚拟制造设备(VMD)概念及核心对象(域/有名变量等)。重点阐述IEC61850与MMS的映射关系,包括对象分层映射、服务转换规则及数据类型对应。深入分析ASN.1编解码机制,说明MMS PDU的分层结构与解码流程。最后通过典型报文案例(报告服务、控制服务、文件传输等)和通信初始化流程,展示MMS在智能变电站中的实际应用。全文揭示了MMS作为抽象应用层协议如何通过标

张二狗的博客 728

第4部分-并发工具类与线程池

/ 创建核心线程i++) {while (!break;});throw new IllegalStateException("线程池已关闭");i < 10;i++) {System.out.println("任务 " + taskId + " 执行中,线程: " + Thread.currentThread().getName());try {});Executors框架。

lpfasd123的博客 448

ABAP 平台 SOAUTH2_CLIENT 全面实战:用一套配置,打通 OAuth 2.0 客户端的创建、联调与运维

本文是一份ABAP平台上配置OAuth 2.0客户端的全流程指南,重点介绍新界面SOAUTH2_CLIENT的使用方法。内容涵盖从概念解析到实操步骤,包括四种常见授权方式的选择、证书信任管理、Scope配置以及联调技巧。指南通过两个企业级案例(对接SAP BTP Cloud Integration和SAML 2.0身份联合)展示实际应用场景,并提供了配置清单和伪代码示例。特别强调新界面将客户端配置、令牌测试和校验功能整合的工程化优势,以及与旧事务码OA2C_CONFIG的主要差异。文中多处引用SAP官方文档

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 319

5-2 Java线程

进程与线程 进程是系统资源分配的基本单位,线程是处理机调度的基本单位。 一个进程至少包含一个线程,比如java中运行main方法的主线程。 多线程是在一个程序中同时运行多个子任务。 多线程可以更好地利于系统资源,提高处理机利用率。 创建线程的方式 继承Thread类 实现Runnable接口 实现Callable接口和FutureTask对象创建线程 1)自定义一个类实现Callable接口并实现其中的call()方法来创建线程任务(Callable的call方法类似于Runnable的run方法)

langkeyring的博客 637

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

JUC并发编程

JUC并发编程JUC是java.util.concurrent的简写,即java并发编程工具包。在jdk官方手册中可以看到juc相关的jar包有三个。实现多线程有三种方式:Thread、Runnable、Callable。

m0_47656773的博客 293

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

剖析线程池ThreadPoolExecutor

线程池是一种多线程处理形式,它能够有效地管理和控制线程的创建、执行和销毁,从而降低系统开销,提高资源利用率。在Java中,ThreadPoolExecutor是线程池最常用的实现类,广泛应用于服务器开发、大数据处理、并行计算等领域。ThreadPoolExecutor虽然提供了丰富的功能和灵活性,但在某些特定场景下可能无法满足所有需求。此时,可以通过继承或实现相关接口来扩展和自定义线程池的行为。例如,可以自定义任务队列的实现以支持优先级调度;或者通过扩展ThreadFactory接口来自定义线程的创建过程。

小阳同学的博客 1309

Java并发:线程池详解(ThreadPoolExecutor)

前言 现在在实现异步时,基本都是使用线程池来实现,线程池在工作应用的还是比较频繁的,本文将就线程池的使用、相关原理和主要方法源码进行深入讲解学习。 线程池的基本使用 package com.joonwhee.concurrent; import java.util.ArrayList; import java.util.List; import java.util.concurre...

程序员囧辉 1万+

Java异步编程演进与线程池最佳实践

《Java异步编程演进与线程池最佳实践》 摘要:本文系统剖析Java异步编程的发展历程与技术选型。从早期危险的new Thread()方式入手,指出其资源泄漏、OOM等风险,进而深入讲解线程池(ExecutorService)的核心机制与优势。通过对比分析,提出生产环境必须遵循"线程池替代裸线程"原则,并给出ThreadPoolExecutor参数配置建议。文章还探讨异步与多线程的本质区别,介绍Java21虚拟线程特性及其在IO密集型场景的性能优势。最终总结出异步编程的最佳实践:使用有界

weixin_50055999的博客 919

线程池深度解析:ThreadPoolExecutor底层实现与CompletableFuture异步编程实战

底层原理:深入理解ThreadPoolExecutor的AQS实现和CAS操作性能优化:掌握基于业务场景的参数调优和JVM优化企业应用:学会设计高可用的线程池架构和监控体系响应式编程:掌握CompletableFuture的响应式编程模式。

工一木子 728

Java8异步编程

一、引言 参考Java多线程几种实现方式,我们可以很快速的通过new Thread(...).start()开启一个新的线程,但是这样创建线程会有很多坏处: 每次都要新建一个对象,性能差; 建出来的很多个对象是独立的,缺乏统一的管理。如果在代码中无限新建线程会导致这些线程相互竞争,占用过多的系统资源从而导致死机或者 oom ; 缺乏许多功能如定时执行、中断等。 因此Java给我们提供好一个十分好用的工具,那就是线程池 二、Java线程池 1、Java线程池概述 Java提供了一个工厂类来构造

Shawn的个人博客 5944

Java异步编程,线程池,虚拟线程

(Virtual Threads)是 JDK 实现的轻量级线程,由 JVM 而非操作系统管理。它们与传统的平台线程(Platform Threads,即操作系统线程)形成对比。

weixin_43691132的博客 604

[特殊字符]️ 线程池深度解析:ThreadPoolExecutor底层实现与CompletableFuture异步编程实战

底层原理:深入理解ThreadPoolExecutor的AQS实现和CAS操作性能优化:掌握基于业务场景的参数调优和JVM优化企业应用:学会设计高可用的线程池架构和监控体系响应式编程:掌握CompletableFuture的响应式编程模式。

2401_89221445的博客 933

ThreadPoolExecutor线程池与CompletableFuture线程异步编排

ThreadPoolExecutor线程池与CompletableFuture线程异步编排

zyxzyx666的博客 2303

Java 异步编程——Java内置线程池(Executor 线程池)

默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于 corePoolSize 时,如果一个线程空闲的时间达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。

longool的博客 1733

线程池 ThreadPoolExecutor ForkJoinPool

线程池 ThreadPoolExecutor ForkJoinPool 异步线程执行实现方式 CompletableFuture

仗剑执天涯的专栏 796

ThreadPoolExecutor与异步编程框架(CompletableFuture、Reactor)的结合使用

线程池与异步编程框架(如 CompletableFuture 和 Reactor)的结合是现代 Java 开发中实现高性能、高并发和响应式编程的关键手段。线程池通过避免频繁创建和销毁线程,提升并发性能并控制资源消耗,而异步编程框架则支持非阻塞处理,提高系统吞吐量和响应速度。CompletableFuture 适用于简单的异步任务编排,而 Reactor 则更适合复杂的流式处理和背压控制。在实际应用中,应根据任务类型选择合适的线程池配置,并结合异步框架的特性进行任务编排和调度。通过合理使用线程池和异步编程框架

红血哥 994
上一篇: Linux 命令—— ping、telnet、curl、wget(网络连接相关命令)
下一篇: Java 异步编程——多线程都执行完再继续执行主流程(使用 CountDownLatch 类或调用 ExecutorService 的 invokeAll() 方法)
ShareBeHappy_Qin
博客等级 码龄9年 2321粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值