当前位置:   article > 正文

【多线程(实现线程池)】_线程池实现多线程

线程池实现多线程


前言

在执行并发任务时,往往是通过new Thread() 方法来创建一个新的线程,这样做的弊端会比较多,比如最大的弊端就是频繁创建和销毁线程资源的损耗。所以我们有了线程池,接下来将会好好介绍线程池


一、什么是线程池

从名字来看,我们可以猜测这是一个用来装线程的池子。这时就会想到字符串常量池,字符串常量池是为了提高程序运行效率提出来的,那这个线程池也是为了提高效率吗?当我们知道一个线程的创建和销毁的过程,就会知道创建一个线程开销比创建一个进程消耗要小,但是频繁创建的开销也是不可以忽视的,所以我们有了线程池,提前创建好,放入到这个池子里,就不会出现频繁创建(进入 内核态)。最重要的是线程池也是会提高效率的,当我们调度线程的时候,从线程池拿取线程,是属于用户态操作,而重新去创建一个线程,会涉及到用户态和内核态之间的切换,当进入内核态,那么效率就会比较低了,因为当你把任务交给内核态,内核态不一定立马去完成,它可能晚点才完成,但是交给用户态,会立马去完成,当直接调用线程池存在的线程完成任务,会提高效率。

二、线程池的优点

  1. 创建的线程用了不会销毁,而是重新放入线程池,降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
  2. 提高了响应速度,当任务到来的时候,不需要经历线程的创建,直接从线程池拿线程
  3. 方便线程并发的控制,当线程创建过多是会消耗大量资源的,更是占用过多线程而阻塞系统,从而降低系统的稳定性。线程池能有效管控线程,统一分配、调优,提供资源使用率;
  4. 更强大的功能,线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单。

三、标准库中的线程池

3.1 创建一个线程池

使用 Executors.newFixedThreadPool(n) 能创建出固定包含 n 个线程的线程池.
返回值类型为 ExecutorService
通过 ExecutorService.submit 可以注册一个任务到线程池中

代码如下:

ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
   }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.2 Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.

Executors 本质上是 ThreadPoolExecutor 类的封装.

代码演示:

public class Demo16 {
    public static void main(String[] args) {
        //创建一个含有10个线程的线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        //添加20个任务到线程池中
        for (int i = 0; i < 20; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("嗨,threadpool");
                }
            });
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

四、线程池的实现

1.创建一个阻塞队列用来存放任务

    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
  • 1

2.描述一个工作线程,这个线程的工作是从任务队列拿到任务执行

    static class Worker extends Thread{
        BlockingQueue<Runnable> queue = null;
        //构造方法拿到外面的任务队列
        public Worker(BlockingQueue queue) {
            this.queue = queue;
        }
        @Override
        public void run() {
            while (true) {
                try {
                    Runnable runnable = queue.take();  //如果为空会进入阻塞
                    runnable.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3.创建一个链表结构存放线程

    private List<Thread> workers = new ArrayList<>();
  • 1

4.构造方法确定所添加的核心线程数并创建

    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Worker worker = new Worker(queue); //这是准备好了
            worker.start();   //只有进入了start线程才被创好
            workers.add(worker);  //添加到链表上
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.创建一个方法添加任务到队列中

    public void submit(Runnable runnable){
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这样一个线程池就完整的实现了

总代码如下:

class MyThreadPool {
    //创建一个阻塞队列用来存放任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();


    //描述一个线程,这个线程的工作是从任务队列拿到任务执行
    static class Worker extends Thread{
        BlockingQueue<Runnable> queue = null;
        //构造方法拿到外面的任务队列
        public Worker(BlockingQueue queue) {
            this.queue = queue;
        }
        @Override
        public void run() {
            while (true) {
                try {
                    Runnable runnable = queue.take();  //如果为空会进入阻塞
                    runnable.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    //创建一个链表结构存放线程
    private List<Thread> workers = new ArrayList<>();

    //构造方法确定所添加的核心线程数
    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Worker worker = new Worker(queue); //这是准备好了
            worker.start();   //只有进入了start线程才被创好
            workers.add(worker);  //添加到链表上
        }
    }
    //创建一个方法添加任务到队列中
    public void submit(Runnable runnable){
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Demo12 {
    public static void main(String[] args) {
        MyThreadPool pool = new MyThreadPool(10);
        for (int i = 0; i < 20; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("嗨,mythreadpool");
                }
            });
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

测试结果:
在这里插入图片描述

五、线程池核心参数介绍

在这里插入图片描述

  1. corePoolSize 线程池核心线程数大小
  2. maximumPoolSize 线程池最大线程数量
  3. keepAliveTime 空闲线程存活时间,TimeUnit存活时间单位,如上图60s
  4. 阻塞队列,用来存放任务
  5. threadFactory 线程工厂,创建线程时用的,一般用来线程命名或做一些定制时使用
  6. handler 拒绝策略(有四个),如上图注释。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/1008804
推荐阅读
相关标签
  

闽ICP备14008679号