多线程—线程池

1.线程池状态

状态名 高3位 接受新任务 处理阻塞队列任务 说明
RUNNING 111 Y Y
SHOUTDOWN 000 N Y 不会接受新的任务,但是会处理阻塞队列的剩余任务
STOP 001 N N 会中断正在执行的任务,并且抛弃阻塞队列任务
TIDYING 010 任务全执行完毕,活动线程为0即将进入终结
TERMINATED 011 终结状态

从数字上比较:TERMINATED> TIDYING> STOP >SHOUTDOWN >RUNNING

(因为int最高位事符号位,所以RUNNING的大小是负数)

2. 构造方法

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize:核心线程数(最多保留的线程数)。

  • maximumPoolSize:最大线程数目(maximumPoolSize - corePoolSize = 急救线程数目)。

  • keepAliveTime:急救线程生存时间。

  • unit:急救线程生存时间单位。

  • workQueue:阻塞队列。

  • threadFactory:线程工厂,可以为线程创建时起名字。

  • handler:拒绝策略。

3.工作方式

  • 当线程池里面有剩余的核心线程时,新来的任务会被核心线程执行。

  • 当核心线程数没有空闲时,线程池会判断是否可以创建急救线程来处理任务,如果可以创建,就急救线程处理。

  • 当线程数没用空闲时(核心线程和急救线程都没有空闲),新来的任务就会进入阻塞队列,阻塞队列分为有界队列和无界队列。

  • 如果超过了有界队列的size,线程池就会执行相应的拒绝策略。

  • 当有线程空闲下来之后,有界队列里的任务就会被执行。

  • 当急救线程执行完任务后,在生存时间内没有新的任务执行,就会关闭线程。,核心线程不会关闭。

4.拒绝策略

        JDK提供的四种策略:

  • AbortPolicy:让调用者抛出RejectedExecutionException异常,这是默认策略。

  • CallerRunstPolicy:让调用者运行任务。

  • DiscardtPolicy:放弃本次任务。

  • DiscardtOldestPolicy:放弃队列中最早的任务,本次任务取而代之。

    其它框架的拒绝策略:

  • Dubbo:在RejectedExecutionException异常之前会记录日志,并dump线程栈信息,方便定位问题。

  • Netty:创建一个新线程来执行。

  • ActiveMQ:带超时的,等待一段时间尝试放入队列。

  • PinPoint:使用的是拒绝策略链(是一个集合,里面装着多种拒绝策略),会逐一尝试每种拒绝策略。

四大线程池 :

  1. newFixedThreadPool 固定线程池

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  • 该线程池核心线程数==最大线程数,就是没有急救线程,因此也无需生存时间。

  • 阻塞队列是无界的,可以放任意数量的任务。

  • 当对了任务过多时,可能会导致OOM(Out Of Memory)

  • 适用于任务量已知,相对耗时的任务。

2. newCachedThreadPool 缓冲线程池

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
  • 核心线程数是0,最大线程数是Integer.MAX_VALUE。

  • 全部都是急救线程,急救线程的生存时间是60s。

  • 急救线程可以无线创建。

  • 队列实现采用的是SynchronousQueue<Runnable>(),它没有容量,线程来取一个任务,就往里面放一个任务,如果没有线程来取,那么就放不进去。

  • 整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕时,线程空闲一分钟后就释放线程。

  • 适合任务数比较密集,但每个任务执行时间较短的情况。

3. newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • 只有一个核心线程,不能修改线程数量,任务串行执行。

  • 多个任务时,进入阻塞队列排,队列是无界的,任务执行完毕,唯一的线程也不会被释放。

  • 当唯一核心线程执行任务失败时,该线程没有任何补救措施,但是线程池还会创建一个新的线程执行任务,保证线程池的正常工作。

4.newScheduledThreadPool

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
  • 如果只有一个线程,那么所有任务串行执行。

  • 串行执行时,一个线程出现异常,不会影响其他的线程的执行,这是和Timer最大的区别。

  • 不仅可以让任务延时执行,还能定时执行任务,就是设置时间间隔,让任务每隔一段时间就执行一次。

定时任务方法:

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,
                                               TimeUnit unit);
  • command:任务对象。

  • initialDelay:延时的时间。

  • period:时间间隔。

  • unit:时间单位。

提交任务方法:

execute():提交任务,没有返回值。

void execute(Runnable command);

submit():提交任务task,用返回值future获得任务执行结果。

Future<T> submit(Callable<T> task)

代码示例:

   public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(2);
		//Callable接口只有一个抽象方法,所以可以使用lambda表达式
        Future<String> submit = pool.submit(() -> {
            Thread.sleep(1000);
            return "ok";
        });
		//打印结果
        System.out.println(submit.get());
    }

invokeAll():

//没有设置超时
List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throw InterruptedException;

//设置超时
List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout,TimeUnit unit) throw InterruptedException;

代码示例:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(2);
		
        List<Future<String>> futures = pool.invokeAll(Arrays.asList(
                () -> {
                    return "1";
                },
                () -> {
                    return "2";
                }
        ));
    	//增强for循环
        futures.forEach(v -> {
            System.out.println(v);
        });
    }

invokeAny():提交tasks任务中,返回最先执行完的一个线程的结果,其它线程的任务全部取消

//不带超时的
T invokeAny(Collection<? extends Callable<T>> tasks) throw InterruptedException;

//带超时的
T invokeAny(Collection<? extends Callable<T>> tasks,long timeout,TimeUnit unit) throw InterruptedException, ExecutionException, TimeoutException;

代码示例:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(2);

        String s = pool.invokeAny(Arrays.asList(
                () -> {
                    return "1";
                },
                () -> {
                    return "2";
                }
        ));
    	//输出的值是最先执行完的那个线程的返回值
        System.out.println(s);
    }

关闭线程池方法:

  • shutdown():线程池状态变为SHUTDOWN,不会接收新的任务,但已提交任务会执行完,此方法不会阻塞调用线程的执行。

    源码展示:

    public void shutdown() {
        //给线程池加锁
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            //更改线程池状态
            advanceRunState(SHUTDOWN);
            //打断空闲线程
            interruptIdleWorkers();
            onShutdown(); 
        } finally {
            //解锁
            mainLock.unlock();
        }
        //尝试终结,线程池关闭,如果还有线程在运行,也会关闭线程池,线程运行完之后线程自动关闭
        tryTerminate();
    }

shutdownNow():线程池状态变为STOP,并且不会再接受新的任务,会将队列中的任务返回,并用interrupt打断正在执行的线程。

源码展示:

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        //加锁线程池
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            //更改线程池的状态
            advanceRunState(STOP);
            //打断所有线程
            interruptWorkers();
            //获取队列中的剩余任务
            tasks = drainQueue();
        } finally {
            //解锁
            mainLock.unlock();
        }
        //尝试打断
        tryTerminate();
        return tasks;
    }

其余方法:

  • 只要线程池的状态不是RUNNING,方法就会返回true

boolean isShutdown();
  • 判断线程池的状态是否是 TERMINATED

boolean isTerminated();
  • 如果想在线程池的TERMINATED状态后做一些事情,可以使用该方法等待。

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

Guess you like

Origin blog.csdn.net/qq_42251944/article/details/121017734