Java线程池相关接口

Executor接口

java.util.concurrent包里面有一个接口Executor。这是执行已提交的Runnable任务的对象。该接口的目的是为了解耦任务提交和任务的实际执行(包括线程使用的细节、调度等)。该接口只定义了一个方法execute(Runnable command),用于将任务提交到执行器中并在未来某个时间执行。
直接用法:

package org.hbin.pool.interfaces;

import java.util.concurrent.Executor;

/**
 * Description:  
 * Author: HaleyHu
 * Date: 2025/1/14 10:45
 */
public class ExecutorTest {

    static class RunnableTask1 implements Runnable {
        @Override
        public void run() {

        }
    }

    static class RunnableTask2 implements Runnable {
        @Override
        public void run() {

        }
    }

    public static void main(String[] args) {
        Executor anExecutor = new Executor() {
            @Override
            public void execute(Runnable command) {
                // do something
            }
        };

        Executor executor = anExecutor;
        executor.execute(new RunnableTask1());
        executor.execute(new RunnableTask2());
    }
}

不过,Executor接口并没有严格地要求执行是异步的。简单情况,执行程序可以在调用者线程中立即运行已提交的任务:

import java.util.concurrent.Executor;

public class DirectExecutor implements Executor {
    @Override
    public void execute(Runnable command) {
        command.run();
    }
}

更典型的是,任务在调用者线程之外的某个线程中执行的。以下执行程序为每个任务生成一个新线程。

import java.util.concurrent.Executor;

public class ThreadPerTaskExecutor implements Executor {
    @Override
    public void execute(Runnable command) {
        new Thread(command).start();
    }
}

许多Executor实现对任务的调度方式和时间施加了某种限制。下面的执行器将任务提交序列化到第二个执行器,说明了复合执行器。

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;

public class SerialExecutor implements Executor {
    private Executor executor;
    private Queue<Runnable> tasks;
    private Runnable active;

    public SerialExecutor(Executor executor) {
        this.executor = executor;
        this.tasks = new ArrayDeque<>();
    }

    @Override
    public void execute(Runnable command) {
        tasks.offer(new Runnable() {
            @Override
            public void run() {
                try {
                    command.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if(active == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if((active = tasks.poll()) != null) {
            executor.execute(active);
        }
    }
}

ExecutorService

一个执行器,它提供管理终止的方法,以及可以生成Future以跟踪一个或多个异步任务进度的方法。
ExecutorService可以关闭,这将导致它拒绝新任务。ExecutorService提供了两种不同的方法来关闭。shutdown()方法将允许先前提交的任务在终止前执行,而shutdownNow()方法则阻止等待的任务启动并尝试停止当前正在执行的任务。终止后,执行者没有正在执行的任务,没有等待执行的任务并且不能提交新任务。应关闭未使用的ExecuteService,以便回收其资源。
方法submit通过创建并返回一个Future来扩展基本方法Executor.execute(Runnable),该Future可用于取消执行和/或等待完成。方法invokeAny和invokeAll执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部任务完成。(类ExecutorCompletionService可用于编写这些方法的自定义变体。)
Executors类为此包中提供的执行器服务提供工厂方法。

使用示例
这是一个网络服务的草图,其中线程池中的线程为传入请求提供服务。它使用预配置的Executors.newFixedThreadPool(int)工厂方法:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NetworkService implements Runnable {
    private final ServerSocket socket;
    private final ExecutorService pool;
    public NetworkService(int port, int poolSize) throws IOException {
        socket = new ServerSocket(port);
        pool = Executors.newFixedThreadPool(poolSize);
    }

    @Override
    public void run() {
        try {
            while(true) {
                pool.execute(new Handler(socket.accept()));
            }
        } catch (IOException e) {
            pool.shutdown();
        }
    }
}

class Handler implements Runnable {
    private final Socket socket;
    public Handler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        // read and service request on socket
    }
}

常用方法:

  • submit()
    提交任务到线程池中执行,并返回一个Future对象,它可以用于判断任务是否完成,以及获取任务的执行结果。
  • shutdown()
    关闭线程池。调用该方法后,线程池不再接收新的任务,但会继续执行已提交的任务直到所有任务执行完毕。
  • shutdownNow()
    立即关闭线程池。调用该方法后,线程池会立即中断所有正在执行的任务,并返回一个列表,包含所有未执行的任务。
  • isShutdown()
    线程池是否关闭
  • isTerminated()
    线程池中的所有任务是否已经执行完毕
  • awaitTermination()
    等待线程池中的所有任务全部执行完毕。该方法会阻塞当前线程,直到所有任务全部执行完毕或等待超时。
  • invokeAll()
  • invokeAny()

ScheduledExecutorService

ScheduledExecutorService是Java中用于调度任务的接口,它是ExecutorService的子接口,扩展了线程池的功能,允许在预定的时间执行任务,也可以周期性地重复执行任务。ScheduledExecutorService的设计初衷是为了解决Timer和TimerTask的缺陷,提供更灵活和可靠的定时调度功能。
常用方法:

  • schedule(Runnable command, long delay, TimeUnit unit)
    在给定的延迟之后运行命令一次。
  • schedule(Callable<V> callable, long delay, TimeUnit unit)
    在给定的延迟之后执行有返回值的任务一次。
  • scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
    在指定的初始延迟后开始执行任务,并按照固定的时间间隔重复执行。当任务的执行时长大于周期,那么下一个任务将在当前任务执行完成后马上执行。
  • scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
    在指定的初始延迟后开始执行任务,并且在每次执行完成后,等待固定的延迟时间再执行下一次。

AbstractExecutorService

它是一个抽象类,实现了ExecutorService接口,提供了线程池的基本实现。它是Java Executor框架的核心类,提供了线程池的基本操作,如提交任务、管理线程池、执行任务等。自定义执行器服务可以扩展AbstractExecutorService并覆盖其方法以提供自己的实现。这使开发人员可以创建适合其特定需求的执行器服务。

ThreadPoolExecutor

ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位标识线程数量。

public class ThreadPoolExecutor extends AbstractExecutorService {
    /**
     * The main pool control state, ctl, is an atomic integer packing
     * two conceptual fields
     *   workerCount, indicating the effective number of threads
     *   runState,    indicating whether running, shutting down etc
     *
     * In order to pack them into one int, we limit workerCount to
     * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
     * billion) otherwise representable. If this is ever an issue in
     * the future, the variable can be changed to be an AtomicLong,
     * and the shift/mask constants below adjusted. But until the need
     * arises, this code is a bit faster and simpler using an int.
     *
     * The workerCount is the number of workers that have been
     * permitted to start and not permitted to stop.  The value may be
     * transiently different from the actual number of live threads,
     * for example when a ThreadFactory fails to create a thread when
     * asked, and when exiting threads are still performing
     * bookkeeping before terminating. The user-visible pool size is
     * reported as the current size of the workers set.
     *
     * The runState provides the main lifecycle control, taking on values:
     *
     *   RUNNING:  Accept new tasks and process queued tasks
     *   SHUTDOWN: Don't accept new tasks, but process queued tasks
     *   STOP:     Don't accept new tasks, don't process queued tasks,
     *             and interrupt in-progress tasks
     *   TIDYING:  All tasks have terminated, workerCount is zero,
     *             the thread transitioning to state TIDYING
     *             will run the terminated() hook method
     *   TERMINATED: terminated() has completed
     *
     * The numerical order among these values matters, to allow
     * ordered comparisons. The runState monotonically increases over
     * time, but need not hit each state. The transitions are:
     *
     * RUNNING -> SHUTDOWN
     *    On invocation of shutdown(), perhaps implicitly in finalize()
     * (RUNNING or SHUTDOWN) -> STOP
     *    On invocation of shutdownNow()
     * SHUTDOWN -> TIDYING
     *    When both queue and pool are empty
     * STOP -> TIDYING
     *    When pool is empty
     * TIDYING -> TERMINATED
     *    When the terminated() hook method has completed
     *
     * Threads waiting in awaitTermination() will return when the
     * state reaches TERMINATED.
     *
     * Detecting the transition from SHUTDOWN to TIDYING is less
     * straightforward than you'd like because the queue may become
     * empty after non-empty and vice versa during SHUTDOWN state, but
     * we can only terminate if, after seeing that it is empty, we see
     * that workerCount is 0 (which sometimes entails a recheck -- see
     * below).
     */
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
}
状态名高3位接收新任务处理阻塞任务队列说明
RUNNING111YY
SHUTDOWN000NY不会接收新任务,但会处理阻塞队列剩余任务
STOP001NN会中断正在执行的任务,并抛弃阻塞队列任务
TIDYING010--任务全执行完毕,活动线程为0即将进入终结
TERMINATED011--终结状态

从数值上看,RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED

这些信息存储在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一,这样就可以用一次cas原子操作进行赋值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值