Java线程池常见方法使用

在实际的编程中,我们通常借助Executor框架实现异步任务。
通常用工具类Executors工具类来帮助我们来创建线程池。

Executors中的常用方法

// ThreadPoolExecutor extends AbstractExecutorService
// AbstractExecutorService implements ExecutorService
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
       
}

参数释义:

  • corePoolSize:线程池的核心线程池大小
  • maximumPoolSize:线程池最大大小
  • keepAliveTime:设置超时时间
  • unit:时间单位
  • workQueue:保存等待执行的任务
  • threadFactory:线程工厂,可以省略,省略的话,那就使用默认的
Executors.newFixedThreadPool( )

newFixedThreadPool( )方法代码如下

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
// newFixedThreadPool( )方法的重载,添加threadFactory参数
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
}

基于源码分析可知:

  1. 线程池的核心线程池大小等于其最大线程池大小
  2. 工作线程永不超时
  3. 达到最大线程池的最大数量,线程池的规模将不再变化

补充:如果某个线程由于发生了未预期的Exception而结束,那么线程池会补充一个新的线程;当我们不再需要该线程池时主动将其关闭。

Executors.newCachedThreadPool( )

newCachedThreadPool( )方法代码如下

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
// newCachedThreadPool( )方法的重载,添加threadFactory参数
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory){
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
}

基于源码分析可知:

  1. 线程池的核心线程池大小设置为0
  2. 线程池的最大大小设置为Integer.MAX_VALUE
  3. 工作线程超时为60s

补充:这种方法创建出来的线程池可以被无限扩展,并且当需求降低时会自动收缩。

Executors.newSingleThreadExecutor( )

newSingleThreadExecutor( )方法代码如下

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}
// newSingleThreadExecutor( )方法的重载,添加threadFactory参数
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
}  

基于源码分析可知:

  1. 是一个单线程的Executor
  2. 工作线程超时为0

补充:它创建单个工作线程来执行任务,如果这个任务异常结束,会创建一个线程来替代。newSingleThreadExecutor( )能确保依照任务在队列中的顺序来串行执行。

Executors.newScheduledThreadPool( )

newScheduledThreadPool( )方法代码如下

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
        return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
}

public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
}    

基于源码分析可知:

  1. 创建了固定长度的线程池
  2. 工作线程超时为0

使用示例

import java.text.DateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TestNewScheduledThreadPool {


    public static void main(String[] args) {

        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        
        // 在任务执行时间小于间隔时间的情况下,程序以起始时间为准则,每隔指定时间执行一次,不受任务执行时间影响。
        scheduleAtFixedRate(service, 1000);
        // 当执行任务时间大于间隔时间,此方法不会重新开启一个新的任务进行执行,而是等待原有任务执行完成,马上开启下一个任务进行执行。此时,执行间隔时间已经被打乱
        scheduleAtFixedRate(service, 6000);

        // 此方法无论任务执行时间长短,都是当第一个任务执行完成之后,延迟指定时间再开始执行第二个任务
        scheduleWithFixedDelay(service, 1000);
        scheduleWithFixedDelay(service, 6000);
    }

    private static void scheduleAtFixedRate(ScheduledExecutorService service, final int sleepTime) {
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                long start = new Date().getTime();
                System.out.println("scheduleAtFixedRate 开始执行时间:" + DateFormat.getTimeInstance().format(new Date()));
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                long end = new Date().getTime();
                System.out.println("scheduleAtFixedRate 执行花费时间=" + (end - start) / 1000 + "m");
                System.out.println("scheduleAtFixedRate 执行完成时间:"
                        + DateFormat.getTimeInstance().format(new Date()));
                System.out.println("======================================");
            }
        }, 1000, 5000, TimeUnit.MILLISECONDS);
    }

    private static void scheduleWithFixedDelay(ScheduledExecutorService service, final int sleepTime) {
        service.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                long start = new Date().getTime();
                System.out.println("scheduleWithFixedDelay 开始执行时间:" +
                        DateFormat.getTimeInstance().format(new Date()));
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                long end = new Date().getTime();
                System.out.println("scheduleWithFixedDelay执行花费时间=" + (end - start) / 1000 + "m");
                System.out.println("scheduleWithFixedDelay执行完成时间:"
                        + DateFormat.getTimeInstance().format(new Date()));
                System.out.println("======================================");
            }
        }, 1000, 5000, TimeUnit.MILLISECONDS);
    }
}

代码来源link

补充
Timer存在一些缺陷,因此应该考虑使用ScheduledThreadPoolExecutor来替代它。

缺陷如下:

  • Timer在执行所有定时任务时只会创建一个线程。
  • 如果TimerTask抛出了一个未检查的异常时,将终止定时线程。Timer也不会恢复线程的执行,而是会错误地认为整个Timer都被取消了。

ExecutorService的声明周期

ExecutorService的生命周期有三种状态:运行关闭已终止
ExecutorService在初始创建时处于运行状态。
shutdown方法将执行平缓的关闭过程:不再接受新的任务,同时等待已经提交的任务执行完成——包括那些还未开始执行的任务。
shutdownNow方法将执行粗暴的关闭过程:它将尝试取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务。将会返回尚未启动的任务清单。
所有任务完成后,ExecutorService将转入终止状态。可以调用awaitTermination( )来等待ExecutorService到达终止状态,或者通过调用isTerminated来轮询ExecutorService是否已经终止。

Callable与Future接口

public interface Callable<V> {
    V call() throws Exception;
}
public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);
   
    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;
   
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

可以看到call( )方法返回一个值或者抛出异常。如果我们起了一个线程,需要得到返回值,但是这个返回值什么时候返回并不知道,因为是异步的,而且是得到返回值还是捕获了异常也不清楚。这些都需要进行进一步的封装,也就是Future接口。个人认为,Future接口的存在是为了封装对任务执行情况的获取。
Future的get( )是阻塞的。

结合线程池使用
import java.util.concurrent.*;

public class Exam {
    static ExecutorService pool = Executors.newCachedThreadPool();


    class A implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "Hello";
        }
    }

    class B implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("class B");
        }

        @Override
        public String toString() {
            return "B{}";
        }
    }

    class C implements Callable<B> {

        @Override
        public B call() throws Exception {
            return new B();
        }

        @Override
        public String toString() {
            return "C{}";
        }
    }

    public void test() {
        Future<String> result = null;
        Future<B> result2 = null;
        for (int i = 0; i < 5; i++) {
            pool.submit(new B());
            result = pool.submit(new A());
            if (i == 2) {
                result2 = pool.submit(new C());
            }
        }
        try {
            System.out.println(result.get());
            System.out.println(result2.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        Exam e = new Exam();
        e.test();
    }
}

output:
Hello
B{}
class B
class B
class B
class B
class B
结合FutureTask使用
public static void test2(){
        // FutureTask<V> implements RunnableFuture<V> 
        // RunnableFuture<V> extends Runnable, Future<V>
        FutureTask<String> task = new FutureTask<>(new Callable<String>() {

            @Override
            public String call() throws Exception {
                return "ok";
            }
        });
        Thread t = new Thread(task);
        t.start();
        try {
            System.out.println(task.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
}
output:
ok

CompletionService

当向Executor提交多个任务并且希望获得它们在完成之后的结果,如果用FutureTask,可以循环获取task,并调用get方法去获取task执行结果,但是如果task还未完成,获取结果的线程将阻塞直到task完成,由于不知道哪个task优先执行完毕,使用这种方式效率不会很高。在jdk5时候提出接口CompletionService,它整合了Executor和BlockingQueue的功能,可以更加方便在多个任务执行时获取到任务执行结果。

public interface CompletionService<V> {
    // 提交Callable类型的task
    Future<V> submit(Callable<V> task);
    // 提交Runnable类型的task
    Future<V> submit(Runnable task, V result);
    // 获取并移除已完成状态的task,如果目前不存在这样的task,则等待
    Future<V> take() throws InterruptedException;
    // 获取并移除已完成状态的task,如果目前不存在这样的task,返回null;
    Future<V> poll();
    // 获取并移除已完成状态的task,如果在指定等待时间内不存在这样的task,返回null
    Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}

示例:

public class CompletionServiceTest {
    private static ExecutorService executor = Executors.newFixedThreadPool(100);

    public void run() {
        CompletionService<Long> completionService = new ExecutorCompletionService<>(executor);
        final int groupNum = 100000000 / 100;
        for (int i = 1; i <= 100; i++) {
            int start = (i - 1) * groupNum + 1, end = i * groupNum;

            completionService.submit(new Callable<Long>() {
                @Override
                public Long call() throws Exception {
                    Long sum = 0L;
                    for (int j = start; j <= end; j++) {
                        sum += j;
                    }
                    return sum;
                }
            });
        }
        long result = 0L;
        try {
            for (int i = 0; i < 100; i++) {
                // completionService.take()得到是一个Future
                result += completionService.take().get();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("the result is " + result);
    }

    public static void main(String[] args) {
        CompletionServiceTest completionServiceTest = new CompletionServiceTest();
        completionServiceTest.run();
    }
}

参考链接:Link

饱和策略

ThreadPoolExecutor t = new ThreadPoolExecutor(10,20,0,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1000));
// 设置饱和策略
t.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());

四种饱和策略:

  • CallerRunsPolicy:调用者运行策略
  • AbortPolicy:终止策略是默认的饱和策略
  • DiscardPolicy:抛弃策略
  • DiscardOldestPolicy:抛弃最旧的策略
AbortPolicy
public static class AbortPolicy implements RejectedExecutionHandler {
      
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
}
DiscardOldestPolicy

抛弃最旧的策略则会抛弃下一个将被执行的任务,然后尝试重新提交新的任务。不要将此策略和优先级队列放在一起使用,因为将导致抛弃优先级最高的任务。

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
}
DiscardPolicy

抛弃策略会悄悄地抛弃该任务。

 public static class DiscardPolicy implements RejectedExecutionHandler {
        
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
CallerRunsPolicy

调用者运行策略实现了一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。它不会在线程池的某个线程中执行新提交的任务,而是在一个调用了execute的线程中执行该任务。

 public static class CallerRunsPolicy implements RejectedExecutionHandler {
    
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值