java线程池相关

一,四种常见的线程池类型
1.SingleThreadExecutor,单线程化的线程池

//使用Executors 工具类来创建一个单线程的线程池
//单线程线程池,可以保证线程按照顺序执行
ExecutorService singleThread = Executors.newSingleThreadExecutor();
// ExecutorService是Java提供的用于管理线程池的接口,用于管理线程数量和重用线程

//调用execute方法执行任务,方法的参数是Runnable类型
//把需要使用的线程来执行任务,提交给线程池,由线程池来给任务分配线程资源
singleThread.execute(() ->{
     System.out.println("first");
});
singleThread.execute(() ->{
    System.out.println("second");
});
singleThread.execute(() ->{
    System.out.println("third");
});

运行结果:

first
second
third

2.FixedThreadPool,可重用固定个数的线程池

//创建定长线程池,方法参数就是线程池的线程数量 ,是可重用固定个数的线程池
//该线程池不是创建里面有5个线程,而是随着任务的增加而增加
//来了一个线程之后,线程池会创建一个线程,如果后续来了新的线程,并且目前没有空闲线程
//线程池会新创建新的线程来执行新任务,直到新线程池里的线程数量达到5之后,则不会创建新线程

//应用场景: 需要使用多个线程来同时处理任务,并且线程数量不能太多,需要控制
ExecutorService fixThreadPool = Executors.newFixedThreadPool(5);

fixThreadPool.execute(() -> {
try {
       Thread.sleep(10);
} catch (InterruptedException e) {
       e.printStackTrace();
}
      System.out.println("111");
});
fixThreadPool.execute(() -> {
  try {
         Thread.sleep(10);
  } catch (InterruptedException e) {
          e.printStackTrace();
  }
        System.out.println("222");
  });
fixThreadPool.execute(() -> {
   try {
        Thread.sleep(10);
   } catch (InterruptedException e) {
        e.printStackTrace();
   }
        System.out.println("333");
});

输出结果:

222
111
333

线程执行的顺序不是固定的

for(int i=0; i<10; i++){
            fixThreadPool.execute(()->{
                System.out.println("正在执行" + Thread.currentThread().getName());
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

输出结果:

正在执行pool-2-thread-1
正在执行pool-2-thread-3
正在执行pool-2-thread-2
正在执行pool-2-thread-4
正在执行pool-2-thread-5
正在执行pool-2-thread-3
正在执行pool-2-thread-5
正在执行pool-2-thread-1
正在执行pool-2-thread-4
正在执行pool-2-thread-2

线程的数量达到设置的最大数量之后,不会再创建新的线程

3.CachedThreadPool 可以缓存的线程池

//创建一个可以缓存的线程池,不需要参数
//线程数量随着任务数量的变化而变化,任务多则会自动创建线程
//任务少,则会自动回收线程

//系统任务数量不太确定,忽高忽低,任务执行速度较快的应用场景可以使用该线程
//任务数量特别多的项目里不适合使用这种线程池,因为可能导致线程过多,程序挂掉
        ExecutorService cachedThread = Executors.newCachedThreadPool();

        ExecutorService cachedThread = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            cachedThread.execute(() -> {
                System.out.println("正在执行" + Thread.currentThread().getName());

            });
        }
        cachedThread.shutdown();

输出结果:

正在执行pool-2-thread-1
正在执行pool-2-thread-2
正在执行pool-2-thread-3
正在执行pool-2-thread-3
正在执行pool-2-thread-2
正在执行pool-2-thread-1
正在执行pool-2-thread-2
正在执行pool-2-thread-4
正在执行pool-2-thread-5
正在执行pool-2-thread-6

缓存线程池会重复利用之间已经创建的线程

给线程加上睡眠时间,

ExecutorService cachedThread = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            cachedThread.execute(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("正在执行" + Thread.currentThread().getName());

            });
        }

运行结果:

正在执行pool-2-thread-3
正在执行pool-2-thread-1
正在执行pool-2-thread-4
正在执行pool-2-thread-2
正在执行pool-2-thread-6
正在执行pool-2-thread-9
正在执行pool-2-thread-10
正在执行pool-2-thread-5
正在执行pool-2-thread-7
正在执行pool-2-thread-8

因为线程的睡眠时间长会导致缓存池中的线程被回收,所以每次都会创建新的线程


查看源码可以发现,缓存线程中的最大线程数是int整型的最大值

4.ScheduledThreadPool 周期性线程池

//创建一个周期性线程池
        ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1);
        System.out.println("schedule start");
        //延迟10秒,执行线程
        schedule.schedule(() -> {
            System.out.println("aaaaaa");
        }, 10, TimeUnit.SECONDS);

        //周期性执行,可以做一些需要周期性重复执行的任务,比如定时通知,定时清理等任务
        schedule.scheduleAtFixedRate(() -> {
            System.out.println("定时周期执行");
        }, 3, 10, TimeUnit.SECONDS);//延迟3秒后,每10秒执行一次

二,自定义线程池

//第一个参数为核心线程数,第二个是最大线程数,第三个是等待时间
//第四个是时间单位,第五个是等待队列对象,第六个参数是该线程池的拒绝策略
ExecutorService es = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS,
    new LinkedBlockingDeque<>(100), new ThreadPoolExecutor.CallerRunsPolicy());

     es.execute(() -> {
         System.out.println("自己创建的线程池");
     });

源码

三,停止线程池
线程池在程序运行过程中不会自动关闭,需要我们添加代码手动关闭

1.调用shutdown方法停止线程池
shutdown方法会等待线程中的任务执行完毕再关闭线程池

singleThread.execute(() -> {
            System.out.println("first");
        });
        singleThread.execute(() -> {
            System.out.println("second");
        });
        singleThread.execute(() -> {
            System.out.println("third");
        });

运行结果:

first
second
third

2.调用shutdownNow方法停止线程池
shutdownNow不会等待线程结束,而是立即关闭线程池
运行结果:
first

四,execute()方法和submit()方法的区别

1.execute()方法只用于任务执行,无法获取结果

//execute方法用于执行任务,但是不关注任务的结果
            single.execute(()->{
                System.out.println("execute运行了");
            });

运行结果:execute运行了

2.submit()方法不仅可以执行任务,也可以获取执行结果

public class ThreadPoolTest2 {

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

        //submit方法可以获取到任务执行的结果
        //接收Callable接口的实现类对象
        Future<String> fu = single.submit(new MyCallable());
        //通过future的get方法,来获取任务的结果
        String result = fu.get();
        System.out.println(result);
        
    }
}
//重写Callable接口的run方法
class MyCallable implements Callable<String>{
    @Override
    public String call() throws Exception {
        return "运行结果";
    }
}

输出结果:运行结果

//submit方法也是可以使用lambda表达式的,加上返回值即可
        single.submit(() ->{
            System.out.println("程序又运行了");
            return "我是程序运行结果";
        });

五、在项目中部署线程池

在springboot中部署线程池,需要创建bean
首先,创建一个类,通过方法来创建bean

//这是一个用来管理bean的类,需要使用configuration注解
@Configuration
public class BeanConfig {

    //springboot中,使用方法来创建bean,该方法的返回值类型,就是我们需要创建的bean类型
    @Bean
    public ExecutorService threadPool(){
        int coreSize = 5;
        int maxSize = 10;
        int time = 10;
        ExecutorService es = new ThreadPoolExecutor(coreSize, maxSize,time,
                TimeUnit.SECONDS, new LinkedBlockingDeque<>(100));
        return es;
    }
}

在service层或者contrller层中注入

@Resource(name = "threadPool")
    private ExecutorService executorService;

如果我们创建了多个bean,多个线程池,那么可以通过name来区分这些线程池,name就是我们在bean中定义的创建线程池的方法名

之后,我们就可以通过对象名来调用线程池了

 //使用线程池来执行任务
            executorService.execute(() ->{
                asyncFetch(body, url, novel.getId());
            });

参考博文:https://blog.csdn.net/hnd978142833/article/details/80253784

posted @ 2021-01-22 22:04  TidalCoast  阅读(90)  评论(0编辑  收藏  举报