【JavaEE】多线程之线程池

目录

1.线程池基础知识

1.1线程池概念

1.2线程池使程序更高效的原理

​2.标准库中的线程池

2.1线程池的继承关系

2.2线程池的构造方法 

2.3线程池的使用

2.线程池的优点 

3.线程池的模拟实现


1.线程池基础知识

1.1线程池概念

 在多线程当中,并发程度会不断提升,随之带来的是线程频繁的创建和销毁,此时程序的开销就会变大,为了改善这个现象,就引入了线程池,程序将创建好的线程放入到“线程池”中,当程序需要用时,可以直接在这个“池子”中取出来,线程用完之后再放入到“池子”中。这样在就不会再频繁的创建和销毁了,可以使程序开销减小,效率更高。

1.2线程池使程序更高效的原理

在创建、销毁线程时我们时是交给操作系统内核来完成的, 而我们使用线程池调度是以用户态实现的。

如果将任务交给操作系统的内核去完成,它的完成时间是不可控的,这是为什么呢?原因在于:内核需要对多个任务提供服务,在你交给它任务时,它可能无法仅仅单一的完成你交与的任务,可能还需要完成别人交与它的任务。

而用户态就不一样了,用户态就仅会对自己的任务负责,因此效率更高,开销更小。

🐣下面这个例子可以让你更加容易理解(银行办事): 

有一天你去银行办理业务,排队排了好久,工作人员问你:拿身份证复印件了吗?你心想:卧槽还要这个。你回答:没有拿。然后工作人员说,没关系,你有两个选择:

1.我给你打印然后办理。(相当于内核态)

2.你自己打印然后给我。 (相当于用户态)

选择1:当让工作人员打印,可能他手头上出了你的打印东西在这个事还有其他的事,因此让他帮打印的话,打印好就不知道事猴年马月了。

选择2: 我自己直接去大厅的复印机这里,直接就打印好了,然后拿给工作人员,办完就跑。

很明显,在1、2选择中,选择2更高效,这也就是为什么线程池更高效了。 

2.标准库中的线程池

2.1线程池的继承关系

2.2线程池的构造方法 

Java中提供了线程池的标准类(ThreadPoolExecutor),构造方法(常用的一个)如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

对构造方法中参数解读

1. corePoolSize(核心线程);相对于公司的正式员工,签了劳动合同的,不能够随意开除。

2.maximumPoolSize (最大线程数):线程最多不能超过这这个数,最大线程数=核心线程+非核心线程;非核心线程就相当于公司招来的实现生。

3.keepAliveTime(非核心线程的存活时间):如果找来的实习生一直摸鱼没有活干,超过这个时间就将他炒鱿鱼。

4.unit:上方存活时间的单位

5.workQueue(用于缓存未执行的任务):线程的任务队列,通过submit方法加入的任务进入这个队列

6.threadFactory(线程工厂):线程的创建方案。

7.handler(拒绝策略):当任务对象线程满了的话,应该如何做出回答。

在Java总handler有四种拒绝策略:

    • Modifier and TypeClass and Description
      static class ThreadPoolExecutor.AbortPolicy

      被拒绝的任务的处理程序,抛出一个 RejectedExecutionException

      static class ThreadPoolExecutor.CallerRunsPolicy

      一个被拒绝的任务的处理程序,直接在 execute方法的调用线程中运行被拒绝的任务,除非执行程序已经被关闭,否则这个任务被丢弃。

      static class ThreadPoolExecutor.DiscardOldestPolicy

      被拒绝的任务的处理程序,丢弃最旧的未处理请求,然后重试 execute ,除非执行程序关闭,在这种情况下,任务被丢弃。

      static class ThreadPoolExecutor.DiscardPolicy

      被拒绝的任务的处理程序静默地丢弃被拒绝的任务。

2.3线程池的使用

线程池在实现时,我们需要用到工具类Executors,调用里面的静态方法来完成创建的,他的所有方法的返回值都是ExecutorService。

import java.util.concurrent.*;

public class testDemo {

    public static void main(String[] args) {
        //创建一个固定数量的线程池
        // 1. 创建一个操作无界队列且固定大小线程池
        ExecutorService pool1 = Executors.newFixedThreadPool(10);

        //线程池中线程的数量是动态变化的
        // 2. 用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将收回并移出缓存
        ExecutorService pool2 = Executors.newCachedThreadPool();

        //线程池中只有一个线程
        // 3. 创建一个操作无界队列且只有一个工作线程的线程池
        ExecutorService pool3 = Executors.newSingleThreadExecutor();

        //线程池中只有一个线程+定时器功能
        // 4. 创建一个单线程执行器,可以在给定时间后执行或定期执行。
        ExecutorService pool4 = Executors.newSingleThreadScheduledExecutor(Executors.defaultThreadFactory());

        //创建一个固定数量的线程池+定时器功能
        // 5. 创建一个指定大小的线程池,可以在给定时间后执行或定期执行。
        ExecutorService pool5 = Executors.newScheduledThreadPool(3, Executors.defaultThreadFactory());

        // 6. 创建一个指定大小(不传入参数,为当前机器CPU核心数)的线程池,并行地处理任务,不保证处理顺序
        ExecutorService pool6 = Executors.newWorkStealingPool();

        // 7. 自定义线程池
        ExecutorService pool7 = new ThreadPoolExecutor(3,
                10,
                10000,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
    }
    
}

2.线程池的优点 

  1. 降低资源消耗:减少线程的创建和销毁带来的性能开销。
  2. 提高响应速度:当任务来时可以直接使用,不用等待线程创建
  3. 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。

3.线程池的模拟实现

  • 核心操作为 submit, 将任务加入线程池中
  • 使用 Worker 类描述一个工作线程. 使用 Runnable 描述一个任务.
  • 使用一个 BlockingQueue 组织所有的任务
  • 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
  • 指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增线程了.
import java.util.concurrent.LinkedBlockingQueue;

class Worker extends Thread {
    private LinkedBlockingQueue<Runnable> queue = null;
    public Worker(LinkedBlockingQueue<Runnable> queue) {
        super("worker");
        this.queue = queue;
    }
    @Override
    public void run() {
        // try 必须放在 while 外头, 或者 while 里头应该影响不大
        try {
            while (!Thread.interrupted()) {
                Runnable runnable = queue.take();
                runnable.run();
            }
        } catch (InterruptedException e) {
        }
    }
}
public class MyThreadPool {
    private int maxWorkerCount = 10;
    private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue();
    public void submit(Runnable command) throws InterruptedException {
        if (queue.size() < maxWorkerCount) {
            // 当前 worker 数不足, 就继续创建 worker
            Worker worker = new Worker(queue);
            worker.start();
        }
        // 将任务添加到任务队列中
        queue.put(command);
    }
}

严重线程池是否模拟实现完成 代码运行如下:

public static void main(String[] args) throws InterruptedException {
        MyThreadPool myThreadPool = new MyThreadPool();
        for (int i = 0; i < 10; i++) {
            int j=i;
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello"+j);
                }
            });
            Thread.sleep(1000);
        }

运行结果如下: 


写在最后:

🐣🐣🐣以上就是本文全部内容,如果对你有所帮助,希望能留下你的点赞+关注,我会更加努力的更新内容!非常感谢🧡🧡🧡

若本篇文章有错误的地方,欢迎大佬们指正!

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃大白菜  

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值