线程池的使用例子

  • Post author:
  • Post category:其他


—- 原文有参看其他博主博客 —

​ 在Java中提供了Executors类可以帮助我们快速构建一些线程池,下面是4种线程池的基本使用例子

  • MyTaskThread 类

    /**
     * MyTaskThread类,实现Runnable接口
     * @Author fx
     * @date 2019/08/16
     */
    public class MyTaskThread implements Runnable {
    
    
        @Override
        public void run() {
            // 这里写你的业务逻辑
            // ...
            System.out.println(Thread.currentThread().getName() + "线程执行了");
        }
    }
    
    


1.newFixThreadPool – 固定线程池

​ 创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

  • corePoolSize与maximumPoolSize相等,即其线程全为核心线程
  • keepAliveTime = 0 该参数默认对核心线程无效,而FixedThreadPool全部为核心线程;
  • workQueue 为LinkedBlockingQueue(无界阻塞队列),队列最大值为Integer.MAX_VALUE,很可能造成OOM(内存溢出)

构造方法:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    /**
     * 此方法用于创建固定的线程池
     */
    public static void fixedPool(){
        // 创建固定数量的线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);
        // 创建线程
        Runnable r1 = new MyTaskThread();
        Runnable r2 = new MyTaskThread();
        Runnable r3 = new MyTaskThread();
        // 将线程放入线程池中执行
        pool.execute(r1);
        pool.execute(r2);
        pool.execute(r3);
        // 待任务执行完成,关闭线程池
        pool.shutdown();
    }

执行结果:

pool-1-thread-2线程执行了
pool-1-thread-3线程执行了
pool-1-thread-1线程执行了

进程已结束,退出代码0

可以看出,放进线程池的3个线程都被执行了



2.newSingleThreadExecutor – 单一线程池

​ 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

  • corePoolSize与maximumPoolSize参数都为1,表示只创建一个线程
  • keepAliveTime = 0 该参数默认对核心线程无效
  • 被FinalizableDelegatedExecutorService包装,表明SingleThreadExecutor被定以后,无法修改,做到了真正的Single

构造方法:

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    /**
     * 此方法用于创建单一线程池
     * 每次调用execute方法,其实最后都是调用了thread-1的run方法。
     */
    public static void singleThreadPool(){
        //创建单一线程池
        ExecutorService pool = Executors.newSingleThreadExecutor();
        // 创建线程
        Runnable r1 = new MyTaskThread();
        Runnable r2 = new MyTaskThread();
        Runnable r3 = new MyTaskThread();
        // 将线程放入线程池中执行
        pool.execute(r1);
        pool.execute(r2);
        pool.execute(r3);
        // 待任务执行完成,关闭线程池
        pool.shutdown();
    }

执行结果:

pool-1-thread-1线程执行了
pool-1-thread-1线程执行了
pool-1-thread-1线程执行了

进程已结束,退出代码0

虽然这里创建了3个线程,然而线程池里执行的其实还是thread1的run方法



3.newCachedThreadPool – 缓存线程池

​ 创建一个可缓存的线程池,可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,当任务数增加时,此线程池又可以

智能

的添加新线程来处理任务。此线程池不会对线程池大小做限制

  • corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE表明线程创建几乎无限制
  • keepAliveTime = 60s,线程空闲60s后自动结束
  • workQueue 为 SynchronousQueue 队列,表明有任务就创建线程,来着不拒

构造方法:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    /**
     * 此方法用于创建缓存线程池
     * 这种方式的特点是:可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们
     */
    public static void cachedPool(){
        ExecutorService pool = Executors.newCachedThreadPool();
        // 创建线程
        Runnable r1 = new MyTaskThread();
        Runnable r2 = new MyTaskThread();
        Runnable r3 = new MyTaskThread();
        Runnable r4 = new MyTaskThread();
        // 将线程放入线程池中执行
        pool.execute(r1);
        pool.execute(r2);
        pool.execute(r3);
        pool.execute(r4);
        // 待任务执行完成,关闭线程池
        pool.shutdown();
    }

执行结果:

pool-1-thread-1线程执行了
pool-1-thread-4线程执行了
pool-1-thread-3线程执行了
pool-1-thread-2线程执行了

进程已结束,退出代码0


4.newScheduledThreadPool – 支持周期任务的线程池

​ 创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求

构造方法:

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
    /**
     * 此方法用于创建支持定时任务的线程池
     */
    public static void schedulePool(){
        // 创建支持周期运行的线程池
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(4);
        // 创建线程
        Runnable r1 = new MyTaskThread();
        Runnable r2 = new MyTaskThread();
        // 将线程放入线程池中执行
        pool.execute(r1);
        // 带有延迟时间的执行,1秒后执行
        pool.schedule(r2,1000,TimeUnit.MILLISECONDS);
        // 待任务执行完成,关闭线程池
        pool.shutdown();
    }

执行结果

pool-1-thread-1线程执行了
// 这里中间间隔了1秒
pool-1-thread-2线程执行了

进程已结束,退出代码0



ThreadPoolExecutor类的使用探讨

​ 在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。

下面是自定义线程池使用例子:

    public static void main(String[] args) {
        // 核心线程池数量
        int corePoolSize = 2;
        // 最大线程池数量
        int maximumPoolSize = 4;
        // 空闲等待时间
        long keepAliveTime = 10;
        // 时间单位,秒
        TimeUnit unit = TimeUnit.SECONDS;
        // 有界阻塞队列
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        // 创建线程的工厂,这里我们自定义一个自己的实现
        ThreadFactory threadFactory = new ThreadFactory() {
            private final AtomicInteger mThreadNum = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
                System.out.println(t.getName() + " has been created");
                return t;
            }
        };
        // 拒绝策略,丢弃任务并抛出RejectedExecutionException
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
        // 预启动所有核心线程
        executor.prestartAllCoreThreads();

        // 创建线程
        for(int i=1;i<=10;i++){
            MyTaskThread taskThread = new MyTaskThread();
            executor.execute(taskThread);
        }

        try {
            // 阻塞主线程
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

执行结果:

my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-1线程执行了
my-thread-2线程执行了
my-thread-2线程执行了
my-thread-4 has been created
my-thread-1线程执行了
my-thread-3线程执行了
my-thread-3线程执行了
my-thread-2线程执行了
my-thread-1线程执行了
my-thread-4线程执行了
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.aynu.fx.test.multithread.MyTaskThread@4d405ef7 rejected from java.util.concurrent.ThreadPoolExecutor@6193b845[Running, pool size = 4, active threads = 0, queued tasks = 0, completed tasks = 9]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at com.aynu.fx.test.multithread.MultiThreadTest.main(MultiThreadTest.java:48)

通过自定义线程池,我们可以更好的让线程池为我们所用,更加适应我的实际场景.



版权声明:本文为zxcbnm7089原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。