一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。

什么是线程,线程和进程的区别是什么?
线程:程序执行流的最小执行单位,是行程中的实际运作单位。
进程:动态的执行过程,活动的实体。
区别:一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者。进程中包含了多个可以同时运行的线程。

线程生命周期

①:new(新建)、②:Runnable(就绪)、③:Running(运行)、④:Blocked(阻塞)、⑤:Terminated(结束)

Java线程池中线程数量跟计算机的线程数量有什么对应关系嘛_线程池


为什么使用线程池?

根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力。线程池核心参数

Java线程池中线程数量跟计算机的线程数量有什么对应关系嘛_守护线程_02

  1. corePoolSize(核心线程大小):最小的线程数量;
  2. maximumPoolSize(线程池最大线程数量):最大线程数量;
  3. keepAliveTime(空闲线程存活时间):空闲线程存活时间;
  4. unit(空间线程存活时间单位):空间线程存活时间单位;
  5. workQueue(工作队列):存放任务队列;
    jdk中包含四种工作队列模式:
    ①ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序。新任务进来,排在队列末尾,有界数组可有效防止资源耗尽问题。当线程数量达到corePoolSize后,再有新任务进来,防止队列末尾,等待调度,队列已满,则创建新线程,若达至maxPoolSize,实行拒绝策略。
    ②LinkedBlockingQuene:基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。该队列近似无界性,当线程数量达到corePoolSize后,再有新任务进来,存入队列中,不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
    ③SynchronousQuene:不缓存任务的阻塞队列,放入一个任务必须等到完成该任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
    ④PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
  6. threadFactory(线程工厂):创建新线程的工厂,可以用来设定线程名、是否为daemon线程(守护线程)等;
    守护线程:一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
  7. handler(拒绝策略):当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,若还有新任务提交进来,执行该策略。
    jdk中包含四种拒绝策略:
    ①AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满;
    ②DisCardPolicy:不执行新任务,也不抛出异常;
    ③DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行;
    ④CallerRunsPolicy:直接调用execute来执行当前任务。

优势:
1. 线程和任务分离,提升线程重用性;
2. 控制线程并发数量,降低服务器压力,统一管理所有线程;
3. 提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
劣势:
1. 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
2. 多线程需要协调和管理,所以需要CPU时间跟踪线程;
3. 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
4. 线程太多会导致控制太复杂,最终可能造成很多Bug;

什么是单线程和多线程?
单线程:只有一个主线程的程序,称作单线程程序。每个正在运行的程序(即进程),至少包括一个线程,这个线程叫主线程。
多线程:拥有多个线程的程序,称作多线程程序。可以根据需要开辟若干子线程,子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行。

自定义线程池-实现

  1. 编写任务类(MyTask),实现Runnable接口;
/*
    需求:
        自定义线程池练习,这是任务类,需要实现Runnable;
        包含任务编号,每一个任务执行时间设计为0.2秒
 */
public class MyTask implements Runnable{

    private int id;
    //由于run方法是重写接口中的方法,因此id这个属性初始化可以利用构造方法完成

    public MyTask(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println("线程:"+name+" 即将执行任务:"+id);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程:"+name+" 完成了任务:"+id);
    }

    @Override
    public String toString() {
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}
  1. 编写线程类(MyWorker),用于执行任务,需要持有所有任务;
/*
    需求:
        编写一个线程类,需要继承Thread类,设计一个属性,用于保存线程的名字;
        设计一个集合,用于保存所有的任务;
 */
public class MyWorker extends Thread{

    private String name;//保存线程的名字
    
    private List<Runnable> tasks;
    
    //利用构造方法,给成员变量赋值
    public MyWorker(String name, List<Runnable> tasks) {
        super(name);
        this.tasks = tasks;
    }

    @Override
    public void run() {
       //判断集合中是否有任务,只要有,就一直执行任务
        while (tasks.size()>0){
            Runnable r = tasks.remove(0);
            r.run();
        }
    }
}
  1. 编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;
/*
    这是自定义的线程池类;

    成员变量:
        1:任务队列   集合  需要控制线程安全问题
        2:当前线程数量
        3:核心线程数量
        4:最大线程数量
        5:任务队列的长度
    成员方法
        1:提交任务;
            将任务添加到集合中,需要判断是否超出了任务总长度
        2:执行任务;
            判断当前线程的数量,决定创建核心线程还是非核心线程
 */
public class MyThreadPool {
    // 1:任务队列   集合  需要控制线程安全问题
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
    //2:当前线程数量
    private int num;
    //3:核心线程数量
    private int corePoolSize;
    //4:最大线程数量
    private int maxSize;
    //5:任务队列的长度
    private int workSize;

    public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
        this.corePoolSize = corePoolSize;
        this.maxSize = maxSize;
        this.workSize = workSize;
    }

    //1:提交任务;
    public void submit(Runnable r){
        //判断当前集合中任务的数量,是否超出了最大任务数量
        if(tasks.size()>=workSize){
            System.out.println("任务:"+r+"被丢弃了...");
        }else {
            tasks.add(r);
            //执行任务
            execTask(r);
        }
    }
    
    //2:执行任务;
    private void execTask(Runnable r) {
        //判断当前线程池中的线程总数量,是否超出了核心数,
        if(num < corePoolSize){
            new MyWorker("核心线程:"+num,tasks).start();
            num++;
        }else if(num < maxSize){
            new MyWorker("非核心线程:"+num,tasks).start();
            num++;
        }else {
            System.out.println("任务:"+r+" 被缓存了...");
        }
    }

}
  1. 编写测试类(MyTest),创建线程池对象,提交多个任务测试;
/*
    测试类:
        1: 创建线程池类对象;
        2: 提交多个任务
 */
public class MyTest {
    public static void main(String[] args) {
        //1:创建线程池类对象;
        MyThreadPool pool = new MyThreadPool(2,4,20);
        //2: 提交多个任务
        for (int i = 0; i <30 ; i++) {
            //3:创建任务对象,并提交给线程池
            MyTask my = new MyTask(i);
            pool.submit(my);
        }
    }
}