阶段二-Day06-线程池

一.线程池

1.1.线程池的概念

用来装载线程的容器就叫做线程池

1.2 线程池的作用

让创建好的线程得以复用,避免由于频繁的创建销毁线程对系统的资源消耗

1.3 线程池的使用

 创建任务类

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}

创建测试类,里面创建线程池对象

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        /*
        public static ExecutorService newCachedThreadPool()
        创建一个没有上限的线程池
        public static ExecutorService newFixedThreadPool (int nThreads)
        创建有上限的线程池
        */
        //1.获取线程池对象
        ExecutorService pool1 = Executors.newFixedThreadPool(3);

        //2.提交任务
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());

        //3.销毁线程池
        //pool1.shutdown();
    }
}

1.4 线程池内部的核心元素

 分别代表

核心线程数量                                int corePoolSize

线程池中最大线程的数量              int maximumPoolSize

空闲时间(多长时间)                      long keepAliveTime

空闲时间(单位,例如:秒)                TimeUnit unit

阻塞队列(等待队列)                      BlockingQueue<Runnable> workQueue

创建线程的方式                            ThreadFactory  threadFactory

拒绝方案                                       RejectedExecutionHandler  handler

1.5 线程池的四个情况  

1.5.1 情况一

线程提交的任务数量等于核心线程数量

当提交三个任务时,线程池中就会创建3个线程去执行这3个任务

1.5.2 情况二

线程提交的任务数量大于核心线程数量

先创建3个核心线程,去处理前3个任务。剩余没有处理的任务前往等待区等候。等到3个核心线程把任务处理完了,归还到线程池中处于空闲状态时,再去执行等待区的未被执行的任务。

1.5.3 情况三

先创建3个核心线程,去处理前3个任务。剩余没有处理的任务前往等待区等候。再多的任务需要创建2个临时线程去执行这2个任务。(临时线程 = 最大线程 - 核心线程)

1.5.4 情况四

 先创建3个核心线程,去处理前3个任务。剩余没有处理的任务前往等待区等候。再多的任务需要创建3个临时线程去执行这3个任务。因为临时线程也无法满足任务的量,剩余的任务就会被拒绝,不执行

二.手写一个简单的线程池

2.1 编写线程任务类-MyTask

用来创建线程执行的任务

public class MyTask implements Runnable{
    /**
     * 需求:
     * 设计一个线程任务类-包含任务编号,每一个任务执行时间设计为0.2秒
     */
    private int 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 +
                '}';
    }
}

2.2 等待任务队列-MyWorker

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();
        }
    }
}

2.3 自定义的线程池-MyThreadPool

public class MyThreadPool {
    /**
     * 需求:
     * 设计一个线程池类
     * 1.成员变量:
     * 1.1 任务队列  集合  需要控制线程安全问题
     * 1.2 当前线程数量
     * 1.3 核心线程数量
     * 1.4 最大线程数量
     * 1.5 任务队列的长度
     * <p>
     * 2.成员方法:
     * 2.1 提交任务
     * 将任务添加到集合中,需要判断是否超出了任务总长度
     * 2.2 执行任务
     * 判断当前线程的数量,决定创建核心线程还是临时线程
     */
    //1. 设置一个集合 存储线程任务
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
    //2.当前线程的数量
    private int num = 0;
    //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;
    }

    //2.1 提交任务
    public void submit(Runnable r) {//MyThreadPool myThreadPool = new MyThreadPool(corePoolSize:2,maxSize:4,workSize:20);
        if (tasks.size() > workSize) {
            System.out.println("任务:" + r + "被丢弃了");
        } else {
            //添加任务
            tasks.add(r);
            //处理任务
            execTask(r);
        }
    }

    private void execTask(Runnable r) {//MyThreadPool myThreadPool = new MyThreadPool(corePoolSize:2,maxSize:4,workSize:20);
        if (num < corePoolSize) {
            new MyWorker("核心线程:" + num, tasks).start();
            num++;
        } else if (num < maxSize) {
            new MyWorker("临时线程: " + num, tasks).start();
            num++;
        } else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务:" + r + " 被缓存了……");
        }
    }
}

2.4 创建测试类

public class MyTest {
    public static void main(String[] args) {
        //创建线程池对象
        MyThreadPool mp = new MyThreadPool(2,4,10);
        //创建多个任务
        for(int i = 0 ; i < 20 ; i++){
            //创建任务对象,并提交给线程池
            MyTask myTask = new MyTask(i);
            //提交给线程池
            mp.submit(myTask);
        }
    }
}

三.Java内置的线程池

3.1线程池接口-ExecutorService

 3.1.1 创建一个无上限的无参线程池

newCachedThreadPool()

public class Test {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        //执行十次任务
        for (int i = 1; i <= 10 ; i++) {
            final int r = i;
           //使用submit方法将任务给到线程池
            es.submit(new Runnable() {
               @Override
               public void run() {
                   //获取线程的名称
                   String name = Thread.currentThread().getName();
                   System.out.println(name+"执行了任务:"+r);
               }
           });
        }
    }
}

3.1.2  创建一个无上限的有参的线程池

newCachedThreadPool(修改线程)

任务类

public class MyRunnable implements Runnable {
    private int id;
    public MyRunnable(int id){
        this.id = id;
    }

    //任务类,创建一个任务编号,在任务中,打印出是哪个线程正在执行任务
    @Override
    public void run() {
        //获取线程的名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}
public class Test2 {
    public static void main(String[] args) {

        ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {
            int n = 0;
            @Override
            public Thread newThread(Runnable r) {
                //可以修改线程的名字
                return new Thread(r,"自定义的线程"+n++);
            }
        });

        //提交10个任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable(i));
        }
    }
}

3.1.3 创建一个有上限的线程池

newFixedThreadPool(线程的数量)

public class Test2 {
    public static void main(String[] args) {

        //创建一个有3个线程的线程池
        ExecutorService es = Executors.newFixedThreadPool(3);

        es.submit(new Runnable() {
            @Override
            public void run() {
                for(int i = 1 ; i <= 10 ; i++){
                    final int r = i;
                    String name = Thread.currentThread().getName();
                    System.out.println(name+"执行了任务:"+r);
                }
            }
        });
    }
}

newFixedThreadPool(线程的数量,修改线程)

public class Test {
    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(3,new ThreadFactory() {
            int n = 0;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义的线程名称"+n++);
            }
        });
        for(int i = 1 ; i <= 10 ; i++){
            es.submit(new MyRunnable(i));
        }
    }
}

3.1.4 单个线程的线程池  

用的还是之前的任务类

public class Test {
    public static void main(String[] args) {
        ExecutorService es = Executors.newSingleThreadExecutor();
        for(int i = 1 ; i <= 10 ; i++){
            es.submit(new MyRunnable(i));
        }
    }
}

3.1.5 对象的选择

性能模式

newCachedThreadPool()去创建线程池

因为这是一个无上限的线程池,可以创建很多线程去交替执行任务。但是对系统压力较大。

所以如果硬件跟得上,任务执行时又在意性能。那么优先考虑使用newCachedThreadPool()去创建线程池

额定线程数量模式

newFixedThreadPool(3)去创建线程池

因为这是一个可指定线程数量的的线程池,且指定几个就能创建几个。所以在硬件一般,任务执行时只需要额定数量的线程。那么可优先考虑使用newFixedThreadPool(3)去创建线程池

追求安全

newSingleThreadExecutor()去创建线程池

因为这是一个只能创建单线程数量的线程池。即线程池中只有一个线程在执行任务。执行任务时不存在线程安全问题。所以在追求安全的前提下且不追求性能,那么可优先考虑使用单线程newSingleThreadExecutor()创建线程池去执行任务。

3.1.6 ExecutorService 对象方法的使用

 3.1.6.1 shutdown()方法
public class Test {
    public static void main(String[] args) {
        //创建线程对象
        ExecutorService es = Executors.newSingleThreadExecutor();

        //提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable(i));
        }

        //关闭线程池
        es.shutdown();
        //已经关闭线程池了,再次无法继续提交任务,会报错RejectedExecutionException
        es.submit(new MyRunnable(666));
    }
}
3.1.6.2 submit()方法

提交任务的方法

3.2 线程池-ScheduledExecutorService对象

对象的创建:

常用方法 :

任务类:

public class MyRunnable implements Runnable {
    private int id;

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

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //获取线程的名称
        String name = Thread.currentThread().getName();
        System.out.println(name + "执行了任务:" + id);
    }
}
public class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        return 10;
    }
}

 1.

 

public class Test02 {
    public static void main(String[] args) throws Exception {
        //创建一个含有三个线程的延时线程池
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
        ses.schedule(new MyCallable(),2, TimeUnit.SECONDS);
    }
}

2 .

public class Test01 {
    public static void main(String[] args) {
        //创建一个延迟线程池对象
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);

        //创建任务
        //任务线程名为1,延误2个单位,单位为秒
        ses.schedule(new MyRunnable(1),2, TimeUnit.SECONDS);

        //三个线程完成十个任务
        for (int i = 1 ; i <= 10 ; i++) {
            ses.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS);
        }
    }
}

3 .

public class Test03 {
    public static void main(String[] args) {
        //1.获取一个具备延迟执行任务的线程池对象
        ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor();

        //任务的时间在这个period的时间里面,如果任务时间比period大,则为任务时间,比period小,则为period时间
            es.scheduleAtFixedRate(new MyRunnable(1),1,5, TimeUnit.SECONDS);
        System.out.println("over");
    }
}

4.

public class Test04 {
    public static void main(String[] args) {
        //1.获取一个具备延迟执行任务的线程池对象
        ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor();

        //delay加上任务时间为间隔时间
        es.scheduleWithFixedDelay(new MyRunnable(1),1,5, TimeUnit.SECONDS);
        System.out.println("over");
    }
}

3.3 线程池中-Future的理解

3.3.1 线程池中-Future的常用方法

 任务类:

public class MyCall implements Callable<Integer> {
    int i;
    int j;
    public MyCall(int i,int j){
        this.i = i;
        this.j = j;
    }

    @Override
    public Integer call() throws Exception {
        return i + j;
    }
}
public class FutureDemo {
    public static void main(String[] args) throws Exception {
        //创建线程池对象
        ExecutorService es = Executors.newCachedThreadPool();

        //Future相当于接收任务,对任务进行各种判断,相当于控制任务的类
        Future<Integer> f = es.submit(new MyCall(1, 1));

        /*boolean b = f.cancel(true);//取消任务:取消返回true,未取消false
        System.out.println("取消任务的结果是:"+b);*/

        //取消任务后无法在继续执行,会报错CancellationException
        test1(f);

    }
    private static void test1(Future<Integer> f) throws Exception{
        //判断任务是否已经完成
        boolean done = f.isDone();//判断任务是否完成。完成-则返回true,未完成-则返回false
        System.out.println("第一次判断任务是否完成:"+done);

        boolean cancelled = f.isCancelled();//判断任务是否取消
        System.out.println("第一次判断任务是否取消:"+cancelled);

        //如果有必要,等待任务计算完成,其结然后获取任务执行结果
        Integer v = f.get();
        System.out.println("任务执行的结果为:"+v);

        boolean done2 = f.isDone();
        System.out.println("第二次判断任务是否完成:"+done2);

        boolean cancelled2 = f.isCancelled();//判断任务是否取消
        System.out.println("第二次判断任务是否取消:"+cancelled2);
    }
}

四.再次手写线程池

线程池类

public class ThreadPool {
    // 初始化线程个数
    //即默认线程池中线程的个数
    private static final int default_pool_size = 2 ;
    //定义一个长度用来设置线程的个数
    private int poolSize = default_pool_size ;
    //定义一个集合保存任务
    private BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<Runnable>() ;

    //构造方法
    public ThreadPool() {
        //初始化线程
        initThread();
    }

    public ThreadPool(int poolSize) {
        //线程池的数量要大于0
        if (poolSize > 0){
            this.poolSize = poolSize;
            //初始化线程
            initThread();
        }
    }

    //开启线程的方法即初始化线程
    public void initThread(){
        //循环开启每一个线程
        for (int i = 0; i < poolSize; i++) {
            new TaskThread("线程-->" + i).start();
        }
    }

    //提交线程
    public void submit(Runnable runnable){
        try {
            blockingQueue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //定义一个内部类当作线程类用来创建线程
    public class TaskThread extends Thread{

        //构造方法,给线程赋值名称
        public TaskThread(String name){
            super(name);
        }

        @Override
        public void run() {
            while(true){
                try {
                    //从任务集合中获取任务并执行
                    Runnable task = blockingQueue.take();
                    task.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        //创建线程池
        ThreadPool threadPool = new ThreadPool(5);

        //提交任务
        for (int i = 0; i < 10; i++) {
            int y = i;
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "---->>>处理了第"+(y + 1)+"个任务");
                }
            });
        }
    }
}

五.ThreadPoolExecutor创建线程

Executors中的静态方法去创建线程池其实底层都是通过ThreadPoolExecutor构建的

核心线程数量                                int corePoolSize

线程池中最大线程的数量              int maximumPoolSize

空闲时间(多长时间)                      long keepAliveTime

空闲时间(单位,例如:秒)                TimeUnit unit

阻塞队列(等待队列)                      BlockingQueue<Runnable> workQueue

创建线程的方式                            ThreadFactory  threadFactory

拒绝方案                                       RejectedExecutionHandler  handler

5.1线程池的工作原理

 线程池最多可执行的任务数 = 队列容量 + 最大线程数

1.客户端每次提交一个任务,线程池就会在核心线程池中创建一个工作线程来执行这个任务。当核心线程池中的线程已满时,则进入下一步操作。

2.把任务试图存储到工作队列中。如果工作队列没有满,则将新提交的任务存储在这个工作队列里,等待核心线程池中的空闲线程执行。如果工作队列满了,则进入下个流程。

3.线程池会再次在非核心线程池区域去创建新工作线程来执行任务,直到当前线程池总线程数量超过最大线程数时,就是按照指定的任务处理策略处理多余的任务。

5.2 任务拒绝策略

RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,它下面存在4个子类。

ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
ThreadPoolExecutor.DiscardPolicy:   丢弃任务,但是不抛出异常 这是不推荐的做法。
ThreadPoolExecutor.DiscardOldestPolicy:抛弃队列中等待最久的任务 然后把当前任务加入队列中。
ThreadPoolExecutor.CallerRunsPolicy:  调用任务的run()方法绕过线程池直接执行。

案例演示1-演示ThreadPoolExecutor.AbortPolicy任务处理策略

//演示ThreadPoolExecutor.AbortPolicy任务处理策略
public class Test {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 ,
                3 ,
                20 ,
                TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) ,
                Executors.defaultThreadFactory() ,
                new ThreadPoolExecutor.AbortPolicy()) ;

        //最大为3,执行5个任务
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

结果:

丢弃任务并抛出异常

 案例演示2-演示ThreadPoolExecutor.DiscardPolicy任务处理策略

public class Test2 {
    public static void main(String[] args) {
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardPolicy()) ;

        // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用DiscardPolicy这个任务处理策略的时候,控制台不会报错
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

丢弃任务但不报错

 案例演示3-演示ThreadPoolExecutor.DiscardOldestPolicy任务处理策略

public class Test3 {
    public static void main(String[] args) {
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(1, 3, 20, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());


        // 提交5个任务
        for (int x = 0; x < 5; x++) {
            // 定义一个变量,来指定指定当前执行的任务;这个变量需要被final修饰
            final int y = x;
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务" + y);
            });
        }
    }
}

哪个任务等待的时间最长,哪个任务被丢弃

 案例演示4-演示ThreadPoolExecutor.CallerRunsPolicy任务处理策略

public class Test4 {
    public static void main(String[] args) {

        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.CallerRunsPolicy());

        // 提交5个任务
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

调用任务的run()方法绕过线程池直接执行。

六.定时器的用法

Timer定时器,可以在指定的时间执行指定的任务。

public class Test {
    public static void main(String[] args) {
        //创建定时器对象
        /*Timer timer = new Timer();*/
        //为true则为守护线程
        Timer timer = new Timer(true);

        //2) 最常用 的方法schedule()
//        timer.schedule(task,time);  在指定的time时间执行task任务
//        timer.schedule(task, delay);  在延迟delay毫秒以后执行Task任务
//        timer.schedule(task, delay, period);   在firstTime时间第一次执行task任务,
//        以后每隔period毫秒后重复执行一次task任务
        //在3秒后打印一次时间, 以后每隔1秒重复打印一次
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(new Date());
            }
        }, 3000, 1000);

        //main线程
        for (int i = 0; i < 10; i++) {
            System.out.println(i + "-------" + System.currentTimeMillis());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值