多线程并发之线程池Executor

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/J080624/article/details/82888787

【1】常见接口和实现类

① 什么是线程池
首先可以联想一下数据库连接池,Redis中的pool。

线程池提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。


② 常见的体系结构

常见的线程池体系结构:

 java.util.concurrent.Executor : 负责线程的使用与调度的根接口
  	|--ExecutorService 子接口: 线程池的主要接口
 	  |--ThreadPoolExecutor 线程池的实现类
 	  |--ScheduledExecutorService 子接口:负责线程的调度
 		 |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService

如下图所示:
在这里插入图片描述

线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。每个ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。


③ 线程池的创建

为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展钩子(hook)。但是,强烈建议程序员使用较为方便的Executors 工厂方法:

  • Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)
  • Executors.newFixedThreadPool(int)(固定大小线程池)
  • Executors.newSingleThreadExecutor()(单个后台线程)
  • Executors.newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。

需要注意的是,它们均为大多数使用场景预定义了设置,也就是说通常情况下可以直接使用无需额外配置。

如下所示:

ScheduledExecutorService scheduledExecutorService = 
Executors.newScheduledThreadPool(5);

ExecutorService executorService = Executors.newCachedThreadPool();

ExecutorService executorService1 = Executors.newFixedThreadPool(5);

ExecutorService executorService2 = Executors.newSingleThreadExecutor();

【2】线程池使用实例

线程池提交任务的三个方法:

<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
 <T> Future<T> submit(Runnable task, T result);

实例代码如下:

package com.jane.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * Created by Janus on 2018/9/28.
 */
public class TestThreadPool {

    public static void main(String[] args) throws Exception {
        //1.创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //参数为Runnable
        ThreadPoolRunnable threadPoolRunnable = new ThreadPoolRunnable();
        // 2.为线程池中的线程分配任务
//        for (int i = 0; i <10 ; i++) {
//            pool.submit(threadPoolRunnable);
//        }

        //参数为Callable
        ThreadPoolCallable threadPoolCallable = new ThreadPoolCallable();
        List<Future<Integer>> futures = new ArrayList<>();
        for (int i = 0; i <10 ; i++) {
            Future<Integer> future = pool.submit(threadPoolCallable);
            futures.add(future);
        }
        for (Future<Integer> future : futures) {
            System.out.println(future.get());
        }

        //3.关闭线程池
        pool.shutdown();

    }
}

class ThreadPoolRunnable implements  Runnable{

    private int i = 0;

    @Override
    public void run() {
        while(i <= 100){
            System.out.println(Thread.currentThread().getName() + " : " + i++);
        }
    }
}

class ThreadPoolCallable implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum =0;
        for(int i=1;i<=100;i++){
            sum+=i;
        }
        System.out.println(Thread.currentThread().getName() + " : " +sum);
        return sum;
    }
}


【3】线程池调度实例

ScheduledExecutorService使用实例代码如下:

package com.jane.controller;

import java.util.Date;
import java.util.Random;
import java.util.concurrent.*;

/**
 * Created by Janus on 2018/9/29.
 */
public class TestScheduledThreadPool {

    public static void main(String[] args){

        System.out.println("项目启动 : "+new Date());
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

        // 延迟两秒执行ScheduledThread
        ScheduledThread scheduledThread = new ScheduledThread();
        scheduledExecutorService.schedule(scheduledThread,2, TimeUnit.SECONDS);

        //项目启动时延迟一秒,然后以2秒为周期执行
//        ScheduledRunnable scheduledRunnable = new ScheduledRunnable();
//        scheduledExecutorService.scheduleAtFixedRate(scheduledRunnable,1,2,TimeUnit.SECONDS);

        // 上一次结束后 延迟一秒,然后以2秒为周期执行
//        scheduledExecutorService.scheduleWithFixedDelay(scheduledRunnable,1,2,TimeUnit.SECONDS);
    }
}

class ScheduledThread implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int i = new Random().nextInt(100);
        System.out.println(Thread.currentThread().getName()+" : "+i+" , "+new Date());
        return i;
    }
}

class ScheduledRunnable implements  Runnable{

    @Override
    public void run() {
        int i = (int) (Math.random()*1000);
        System.out.println(Thread.currentThread().getName()+" : "+i+" , "+new Date());
    }
}

测试一,单独执行schedule:

 ScheduledThread scheduledThread = new ScheduledThread();
 scheduledExecutorService.schedule(scheduledThread,2, TimeUnit.SECONDS);

效果如下:

项目启动 : Sat Sep 29 10:37:16 CST 2018
pool-1-thread-1 : 69 , Sat Sep 29 10:37:19 CST 2018

在主程序启动2秒后执行,效果正常。


测试二,单独执行scheduleAtFixedRate:

ScheduledRunnable scheduledRunnable = new ScheduledRunnable();
        scheduledExecutorService.scheduleAtFixedRate(scheduledRunnable,1,2,TimeUnit.SECONDS);

效果如下:

项目启动 : Sat Sep 29 10:38:51 CST 2018
pool-1-thread-1 : 475 , Sat Sep 29 10:38:52 CST 2018
pool-1-thread-1 : 444 , Sat Sep 29 10:38:54 CST 2018
pool-1-thread-2 : 37 , Sat Sep 29 10:38:56 CST 2018
pool-1-thread-1 : 619 , Sat Sep 29 10:38:58 CST 2018
pool-1-thread-3 : 296 , Sat Sep 29 10:39:00 CST 2018
pool-1-thread-3 : 304 , Sat Sep 29 10:39:02 CST 2018

主程序启动,一秒延迟后,以2秒为间隔周期执行。


测试三,单独执行scheduleWithFixedDelay:

ScheduledRunnable scheduledRunnable = new ScheduledRunnable();
 scheduledExecutorService.scheduleWithFixedDelay(scheduledRunnable,1,2,TimeUnit.SECONDS);

效果如下:

项目启动 : Sat Sep 29 10:42:41 CST 2018
pool-1-thread-1 : 473 , Sat Sep 29 10:42:42 CST 2018
pool-1-thread-1 : 667 , Sat Sep 29 10:42:44 CST 2018
pool-1-thread-2 : 788 , Sat Sep 29 10:42:46 CST 2018
pool-1-thread-1 : 836 , Sat Sep 29 10:42:48 CST 2018
pool-1-thread-3 : 457 , Sat Sep 29 10:42:50 CST 2018
pool-1-thread-3 : 923 , Sat Sep 29 10:42:52 CST 2018
pool-1-thread-3 : 120 , Sat Sep 29 10:42:54 CST 2018

看起来和测试二一样?


测试四,添加ScheduledRunnable2 ,run中sleep一秒:

添加一个类ScheduledRunnable2 如下:

class ScheduledRunnable2 implements  Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int i = (int) (Math.random()*1000);
        System.out.println(Thread.currentThread().getName()+" : "+i+" , "+new Date());
    }
}

测试代码如下:

ScheduledRunnable2 scheduledRunnable2 = new ScheduledRunnable2();
scheduledExecutorService.scheduleWithFixedDelay(scheduledRunnable2,1,2,TimeUnit.SECONDS);

效果如下:

项目启动 : Sat Sep 29 10:51:15 CST 2018
pool-1-thread-1 : 521 , Sat Sep 29 10:51:17 CST 2018
pool-1-thread-1 : 334 , Sat Sep 29 10:51:20 CST 2018
pool-1-thread-2 : 388 , Sat Sep 29 10:51:23 CST 2018
pool-1-thread-1 : 528 , Sat Sep 29 10:51:26 CST 2018
pool-1-thread-3 : 862 , Sat Sep 29 10:51:29 CST 2018
pool-1-thread-3 : 312 , Sat Sep 29 10:51:32 CST 2018
pool-1-thread-3 : 534 , Sat Sep 29 10:51:35 CST 2018
pool-1-thread-3 : 452 , Sat Sep 29 10:51:38 CST 2018

启动后,延迟一秒,再sleep一秒,然后打印第一次调用;之后等待上一次结束后以2秒周期间隔再执行。

注意,后面间隔都是三秒,充分说明了第二次调用是在第一次调用结束后(因此每次执行都会sleep 1 秒)!


测试五 ,使用scheduleAtFixedRate调用scheduledRunnable2:

测试代码如下:

ScheduledRunnable2 scheduledRunnable2 = new ScheduledRunnable2();
scheduledExecutorService.scheduleAtFixedRate(scheduledRunnable2,1,2,TimeUnit.SECONDS);

效果如下:

项目启动 : Sat Sep 29 10:57:27 CST 2018
pool-1-thread-1 : 500 , Sat Sep 29 10:57:29 CST 2018
pool-1-thread-1 : 991 , Sat Sep 29 10:57:31 CST 2018
pool-1-thread-2 : 1 , Sat Sep 29 10:57:33 CST 2018
pool-1-thread-1 : 241 , Sat Sep 29 10:57:35 CST 2018
pool-1-thread-3 : 92 , Sat Sep 29 10:57:37 CST 2018
pool-1-thread-3 : 517 , Sat Sep 29 10:57:39 CST 2018

项目启动后,延迟一秒,又sleep 1 秒,然后打印第一次调用;之后以2秒间隔周期进行线程调用!!

由此可见FixedDelay与FixedRate区别:

  • FixedDelay 上一次结束后,等待X秒,执行下一次;
  • FixedRate 两次调用间隔X秒。

参考博文:SpringBoot中定时任务使用

猜你喜欢

转载自blog.csdn.net/J080624/article/details/82888787