线程案列--线程池

一.认识线程池

线程存在的意义:使用进程实现并发编程(进程太重了),此时引入线程.线程叫做"轻量级进程",创建线程比创建进程更高效;销毁线程比销毁进程更高效;调度线程比调度进程更高效,此时,使用多线程就可以在很多时候代替进程来实现并发编程
但频繁创建销毁线程的时候,开销是比较大的
此时Java就提供了线程池(降低创建/销毁线程的开销)

创建/销毁线程,是由操作系统内核完成
从池子里获取/归还池,是用户代码就能实现的,不必交给内核操作

  • 内核态进行的操作都是在操作系统内核中完成的.内核会给程序提供一些API,称为系统调用,程序可以调用系统调用,驱使内核完成一些工作,而系统调用里面的内容是直接和内核的代码相关的,这一部分工作不受程序员自身控制,都是内核自行完成的.
  • 用户态执行的是程序员自己写的代码
相比于内核来说,用户态,程序执行的行为是可控的
如果是通过内核,从系统创建线程,就需要通过系统调用,让内核执行(内核不是只给你一个程序服务,而是给所有的程序都要提供服务).
因此,当使用系统调用,执行的代码的时候,无法确定内核都要做那些工作,整体过程"不可控"的.

二.标准库中的线程池

Executor 是接口(没有实现任何的类),而ThreadPoolExecutor 类就是Executor的实现类,但ThreadPoolExecutor类在使用上不是很方便,在实例化时需要传入多个参数,以及考虑线程的并发数与线程池运行效率有关的参数,所以使用Executors工厂类创建线程对象,该类对创建ThreadPoolExecutor线程池进行封装
  • 使用 Executors.newFixedThreadPool(5) 能创建出固定包含 5 个线程的线程池.
  • 返回值类型为 ExecutorService
  • 通过 submit方法 可以给线程池提交若干个任务.
public class Thread27 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 1000; i++) {
            int n = i;
            pool.submit(() ->{
                System.out.println("hello " + n);
            });
        }
    }
}

运行程序之后,main线程结束,但是整个进程没结束,线程池中的线程都是前台线程.此时会阻止进程的结束

可以认为,这1000子任务,就在一个队列中排队,这5个线程,就依次来取队列中的任务,取一个执行一个,执行完之后再执行下一个

工厂模式 (使用普通的方法来代替构造方法,创建对象)
Executors.newFixedThreadPool(5),使用Executors类的静态方法,直接构造出一个对象,像这样的方法,就称为"工厂方法",提供这个工厂方法的类,Executors就成称为"工厂类".

Executors创建线程池的几种方式

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

线程案列--线程池_第1张图片

2.1构造方法 ThreadPoolExecutor

在这里插入图片描述

corePoolSize 核心线程数
maximumPoolSize 最大线程数
long keepAliveTime 描述了临时工可以摸鱼的最大时间
TimeUnit unit 时间单位(s,ms,分钟)
BlockingQueue workQueue线程池的任务队列
ThreadFactory threadFactory 用于创建线程,线程池是需要创建线程的
RejectedExecutiomHandler handler 描述了线程池的"拒绝策略",也是一个特殊的对象,描述了当线程池任务队列满了,如果继续添加任务会有啥样的行为

ThreadPoolExecutor 相当于把里面的线程分成两类:

  1. 正式员工

  2. 临时工/实习生

     允许正式员工摸鱼,不允许实习生摸鱼,如果实习生摸鱼摸的久了,就会被销毁,
    

    整体的策略:正式员工保底,临时工动态调节

2.2 拒绝策略:

线程案列--线程池_第2张图片

三.实现固定数量的线程池

一个线程池,里面至少有两个大的部分

  1. 阻塞队列
  2. 若干个工作线程
class MyThreadPool{
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    public MyThreadPool(int n){
        //创建出线程
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() ->{
                while (true){
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }
    //注册任务给线程池
    public void submit(Runnable runnable){
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Thread28 {
    public static void main(String[] args) {
        MyThreadPool pool = new MyThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int n = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello  " + n);
                }
            });
        }
    }
}

运行结果
线程案列--线程池_第3张图片

你可能感兴趣的:(JavaEE,java,java-ee,ide)