Java高并发-线程池的原理(架构思路)

最近在学线程池,了解线程池的原理,对以后学习多线程有更好的理解。

线程、线程任务的关系

开启线程,执行任务的流程:

1.创建线程任务;(三种方法)

1.继承Thread类,覆盖重写run()方法,在run方法中填写任务要执行的内容;
2.实现Runnable接口,覆盖重写run()方法,在run方法中填写任务要执行的内容;
3.实行Callable接口,覆盖重写call()方法,在call方法中填写任务要执行的内容;

2.创建Thread类的线程对象;

Thread thread=new Thread();

3.通过线程对象调用start()方法,执行任务体;

thread.start();

为什么要使用线程池

这个问题对所有的池类有相似的解释,例如熟悉的连接池对象池内存池线程池,以线程池为例:

带来的问题

  • 线程大量的创建和销毁,加大了性能的开销,例如每次创建线程,cpu都要做一次内存的分配、调佣内核指令、线程上下文切换。
  • 大量的线程无法控制线程的数量,影响cpu的资源。

好处

  • 减少线程的创建,复用线程。
  • 可观控制线程的创建数量。

线程池的实现原理

实现线程池要解决的问题:

  • 让线程的run方法不执行结束,一直执行。

  • 有任务的时候,线程执行。

  • 没有任务的时候,线程处于休眠状态,让出cpu.

在设计模式中,选用生产者-消费者模式,使用BlockingQueue阻塞队列作为消费者和生产者的中介,可以对生产者和消费者进行阻塞和唤醒。如下图:

在这里插入图片描述

  • 生产者将生产出来的任务放入到队列中,消费者在队列中获取任务进行消费。
    (main方法为生产者,线程池里的线程为消费者,main方法将任务放到线程池的队列中,线程池里的线程获取任务执行)
  • 当队列的值达到队列长度时,生产者会被阻塞,等消费者消费了任务时,唤醒生产者。
  • 当队列的值为空时,消费者会被阻塞,等生产者生产了任务时,唤醒消费者。
    在这里插入图片描述

JDK提供的线程池的具体实现,实现类:

 java.util.concurrent.ThreadPoolExecutor

构造方法:

public ThreadPoolExecutor(int corePoolSize, //(核心线程数)
   int maximumPoolSize,//(最大线程数)
   long keepAliveTime, //(存活时间)
   TimeUnit unit,//(存活单位)
   BlockingQueue<Runnable> workQueue, //(阻塞队列)
   ThreadFactory threadFactory, //(创建线程的工厂)
   RejectedExecutionHandler handler)//(饱和策略)

通过一个例子引出创建线程池的参数:
1.在main方法中提交一个任务到阻塞队列(阻塞队列)中;
2.线程池中已经创建好的线程(创建线程的工厂)对阻塞队列的任务进行消费(核心线程数);
3.当队列的任务量过大时,导致线程消费不过来,这时候开始克隆新的线程,也就是新建新的线程;
4.当队列满时,新建线程也到达了设定的最大值(最大线程数),当main方法继续加入新的任务是,此时会采取一些措施(饱和策略),例如拒绝消费。
5.当队列的任务书很少时,新建出来的线程会在特定的时间销毁(存活时间存活单位)。

使用线程池的例子:

/**
 * @author libo
 * @data 2022/6/1- 23:28
 */
public class PoolDemo1 {
    static ThreadPoolExecutor executorPool = new ThreadPoolExecutor(3,
            5,
            10,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(10),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            int j = i;
            String taskName = "任务" + j;
            executorPool.execute(()->{
                try {
                    TimeUnit.SECONDS.sleep(j);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println(Thread.currentThread().getName() + taskName + "处理完毕");
        }
        executorPool.shutdown();
    }
}

线程池的类型

有四种线程池的类型,可以通过Executors框架创建。

	ExecutorService executorService = Executors.newCachedThreadPool();
    ExecutorService executorService1 = Executors.newFixedThreadPool(4);
    ExecutorService executorService2 = Executors.newSingleThreadExecutor();
    ExecutorService executorService3 = Executors.newScheduledThreadPool(4);

查看每种线程池的构造方法,其实底层都是在创建ThreadPoolExecutor,只是做了一层封装。

	ExecutorService executorService = Executors.newCachedThreadPool();
	
	public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
 ExecutorService executorService1 = Executors.newFixedThreadPool(4);
 
 public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }
  ExecutorService executorService2 = Executors.newSingleThreadExecutor();
  
      public static ExecutorService newSingleThreadExecutor() {
      return new FinalizableDelegatedExecutorService
          (new ThreadPoolExecutor(1, 1,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>()));
  }
    ExecutorService executorService3 = Executors.newScheduledThreadPool(4);
    
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

优化接口实战

处理业务逻辑时,难免会遇到调用几个mapper层接口获取数据进行处理,如果遇到处理方法耗时大且这几个方法没有关联的,可以尝试使用线程池对接口进行优化。

sevice层:
业务需要统计三个参数的值,分别调用三个mapper方法获取数据,比如数据量大,每个方法耗时5秒,按程序串联执行,这需要15秒完成操作。

		//耗时6秒
		Map<String, Object> user = statMapper.getUser(wallet);
		//耗时5秒
        Map<String, Object> amount = statMapper.getAmount(wallet);
        //耗时4秒
        Map<String, Object> allLog = statMapper.getLog(wallet);
        for (String key : user .keySet()) {
            activity.setUser((Long) user.get(key));
        }
        for (String key : amount .keySet()) {
            activity.setAmount((BigDecimal) amount.get(key));
        }
        for (String key : allLog .keySet()) {
            activity.getLog((Long) allLog .get(key));
        }

以上串行处理完程序需要6+5+4=15秒,现在改用线程池来优化接口,使这几个接口异步进行:

		//创建线程池,默认值赋予3
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        //创建CompletionService接口的实现类,用来处理异步的线程,并且接收返回的结果
        CompletionService completionService = new ExecutorCompletionService<Object>(executorService);
        //耗时6秒
        completionService.submit(() -> statMapper.getUser(wallet));
        //耗时5秒
        completionService.submit(() -> statMapper.getAmount(wallet));
        //耗时4秒
        completionService.submit(() -> statMapper.getLog(wallet));

        executorService.shutdown();
		
        for (int i = 0; i < 3; i++) {
        	//阻塞式获取线程执行完毕的结果
            Map<String, Object> map = (Map<String, Object>) completionService.take().get();
            for (String key : map.keySet()) {
                switch (key) {
                    case "user":
                        activity.setUser((Long) map.get(key));
                        break;
                    case "amount":
                        activity.setAmount((BigDecimal) map.get(key));
                        break;
                    case "allLog":
                        activity.setLog((Long) map.get(key));
                        break;
                }
            }
        }

由于程序异步执行,所以只用时6秒就可以完成操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值