• Java之线程池


    线程池

    线程池基本概念

    ​ 线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象;

    ​ 使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力;当然了,使用线程池的原因不仅仅只有这些,我们可以从线程池自身的优点上来进一步了解线程池的好处;

    线程池优势

    1:线程和任务分离,提升线程重用性;
    2:控制线程并发数量,降低服务器压力,统一管理所有线程;
    3:提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;

    线程池应用场景

    应用场景介绍

    • 网购商品秒杀
    • 云盘文件上传和下载
    • 12306网上购票系统等
      只要有并发的地方、任务数量大或小、每个任务执行时间长或短的都可以使用线程池;只不过在使用线程池的时候,注意一下设置合理的线程池大小即可;

    线程池使用

    Java内置线程池

    jdk1.5版本之后才有线程池 ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize, //核心线程数量
                                  int maximumPoolSize,//     最大线程数
                                  long keepAliveTime, //       最大空闲时间
                                  TimeUnit unit,         //        时间单位
                                  BlockingQueue<Runnable> workQueue,   //   任务队列
                                  ThreadFactory threadFactory,    // 线程工厂
                                  RejectedExecutionHandler handler  //  饱和处理机制
    	) 
    { ... }
    

    corePoolSize(核心线程数):当一个任务提交到线程池中时,如果当前运行的线程数量还没达到核心线程数时,就会新开启一个线程来执行当前任务。当超过核心线程数时,需要结合最大线程数以及任务队列等参数。

    maximumPoolSize(最大线程数):控制程序中允许创建出来的最大的线程数,从而可以保证系统能正常运行,保证服务器能运行在一定的压力之下;

    keepAliveTime(最大空闲时间):线程的空闲时间

    unit(时间单位):空闲时间的单位,是个枚举;

    workQueue(任务队列):当核心线程满了之后,新提交进来的任务就会被加入到任务队列中,当任务队列也满了,此时,才会按照最大线程数来创建新的线程。相当于临时缓冲区;

    threadFactory(线程工厂):创建线程的工厂。

    handler(饱和处理机制):拒绝策略,当我们的核心线程数满了,最大线程数也满了,任务队列也满了之后,会执行拒绝策略。

    线程池工作流程

    在这里插入图片描述

    自定义线程池

    参数设计分析
    核心线程数(corePoolSize)

    ​ 核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定,例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个任务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照8020原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理;

    任务队列长度(workQueue)

    ​ 任务队列长度一般设计为:核心线程数/单个任务执行时间X2即可;例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1秒,则队列长度可以设计为200;

    最大线程数(maximumPoolSize)

    ​ 最大线程数的设计除了需要参照核心线程数的条件外,还需要参照系统每秒产生的最大任务数决定:例如:上述环境中,如果系统每秒最大产生的任务是1000个,那么,最大线程数=(最大任务数-任务队列长度)X单个任务执行时间;既: 最大线程数=(1000-200)*0.1=80个;

    最大空闲时间(keepAliveTime)

    ​ 这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值,用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可;

    自定义线程池实现

    任务类

    package com.test.demo01;
    
    /**
     * 任务类
     *
     * @author zhangzengxiu
     * @date 2022/9/22
     */
    public class MyTask implements Runnable {
    
        private int id;
    
        public MyTask(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println("线程:" + threadName + ",即将执行任务:" + id);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程:" + threadName + ",完成执行完成任务:" + id);
        }
    
        @Override
        public String toString() {
            return "MyTask{" +
                    "id=" + id +
                    '}';
        }
    }
    

    线程类

    package com.test.demo01;
    
    import java.util.List;
    
    /**
     * 线程类
     * 保存线程名称的属性;
     * 设计一个集合,用于保存所有的任务
     *
     * @author zhangzengxiu
     * @date 2022/9/22
     */
    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 runnable = tasks.remove(0);
                runnable.run();
            }
        }
    }
    

    线程池类

    package com.test.demo01;
    
    import java.util.Collection;
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * 自定义线程池
     * 成员变量:
     * 1、任务队列:线程安全集合
     * 2、当前线程数
     * 3、核心线程数
     * 4、最大线程数
     * 5、任务队列长度
     * 成员方法:
     * 1、提交:将任务提交到集合中
     * 2、执行任务:判断核心还是非核心线程
     *
     * @author zhangzengxiu
     * @date 2022/9/22
     */
    public class MyThreadPool {
    
        /**
         * 任务队列
         */
        private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<Runnable>());
    
        /**
         * 当前线程数
         */
        private int num;
    
        /**
         * 核心线程数
         */
        private int corePoolSize;
    
        /**
         * 最大线程数
         */
        private int maxSize;
    
        /**
         * 任务对列长度
         */
        private int workSize;
    
        public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
            this.corePoolSize = corePoolSize;
            this.maxSize = maxSize;
            this.workSize = workSize;
        }
    
        /**
         * 提交任务到线程池
         *
         * @param runnable
         */
        public void submit(Runnable runnable) {
            if (tasks.size() >= workSize) {
                System.out.println("任务被丢弃:" + runnable);
            } else {
                tasks.add(runnable);
                //执行任务
                executeTask(runnable);
            }
        }
    
        /**
         * 执行任务
         *
         * @param runnable
         */
        public void executeTask(Runnable runnable) {
            //当前线程数是否小于核心线程数
            if (num < corePoolSize) {
                new MyWorker("核心线程:" + num, tasks).start();
                num++;
            } else if (num < maxSize) {
                //小于最大线程数
                new MyWorker("非核心线程:" + num, tasks).start();
                num++;
            } else {
                System.out.println("任务被缓存:" + runnable);
            }
        }
    
    }
    

    测试类

    package com.test.demo01;
    
    /**
     * 测试类
     *
     * @author zhangzengxiu
     * @date 2022/9/22
     */
    public class MyTest {
    
        public static void main(String[] args) {
            //核心线程
            int coreSize = 2;
            //最大线程数
            int maxSize = 4;
            //任务队列
            int workSize = 20;
            MyThreadPool pool = new MyThreadPool(coreSize, maxSize, workSize);
            //提交多个任务
            for (int i = 1; i <= 100; i++) {
                pool.submit(new MyTask(i));
            }
        }
    
    }
    

    Java内置线程池

    ExecutorService

    ExecutorService接口是java内置的线程池接口,通过学习接口中的方法,可以快速的掌握java内置线程池的基本使用
    常用方法:
    void shutdown() 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
    List shutdownNow() 停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
    Future submit(Callable task) 执行带返回值的任务,返回一个Future对象。
    Future submit(Runnable task) 执行 Runnable 任务,并返回一个表示该任务的 Future。
    Future submit(Runnable task, T result) 执行 Runnable 任务,并返回一个表示该任务的 Future。

    获取ExecutorService可以利用JDK中的Executors 类中的静态方法,

    常用获取方式如下:
    static ExecutorService newCachedThreadPool() 创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建

    • static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
      线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行;
    • static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
    • static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
      创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建。
    • static ExecutorService newSingleThreadExecutor()
      创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
    • static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
      创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建。
    newCachedThreadPool

    测试代码

    package com.test.demo02;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 通过Executors获取ExecutorService
     * newCachedThreadPool()
     * 特点:
     * 1、每次执行任务时,如果之前的线程可用,就使用以前的线程
     * 2、如果之前的线程正在执行任务,则会创建一个新的线程来执行任务
     * 3、且,线程的数量不做限制
     * 4、当线程空闲到一定时间,也会自动销毁,默认空闲时间60s
     *
     * @author zhangzengxiu
     * @date 2022/9/26
     */
    public class MyTest01 {
    
        public static void main(String[] args) {
            //使用工厂获取线程池对象
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 1; i <= 10; i++) {
                //提交任务
                executorService.submit(new MyRunnable(i));
            }
        }
    }
    
    /**
     * 任务类
     */
    class MyRunnable implements Runnable {
    
        private int id;
    
        public MyRunnable(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "执行任务" + id);
        }
    }
    
    newCachedThreadPool(ThreadFactory threadFactory)

    测试代码

    package com.test.demo02;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadFactory;
    
    /**
     * 通过Executors获取ExecutorService
     * newCachedThreadPool(ThreadFactory threadFactory)
     * 可通过参数自定义线程
     *
     * @author zhangzengxiu
     * @date 2022/9/26
     */
    public class MyTest0101 {
    
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() {
                int i = 1;
    
                public Thread newThread(Runnable r) {
                    return new Thread(r, "自定义线程" + i++);
                }
            });
            for (int i = 1; i <= 10; i++) {
                executorService.submit(new MyRunnable0101(i));
            }
        }
    }
    
    /**
     *任务类
     */
    class MyRunnable0101 implements Runnable {
    
        private int id;
    
        public MyRunnable0101(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "执行任务" + id);
        }
    }
    

    newCachedThreadPool

    任务优先,追求性能,对线程数不做限制

    newFixedThreadPool

    测试代码

    package com.test.demo02;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 通过Executors获取ExecutorService
     * newFixedThreadPool
     * 特点:
     * 1、创建线程池对象时,即可指定线程池中线程的数量
     *
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTest02 {
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService executorService = Executors.newFixedThreadPool(3);
            for (int i = 1; i <= 10; i++) {
                //提交任务
                executorService.submit(new MyRunnable2(i));
            }
        }
    }
    
    class MyRunnable2 implements Runnable {
        private int id;
    
        public MyRunnable2(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "执行任务" + id);
        }
    }
    
    newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

    测试代码

    package com.test.demo02;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadFactory;
    
    /**
     * 通过Executors获取ExecutorService
     *
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTest0202 {
    
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService executorService = Executors.newFixedThreadPool(3, new ThreadFactory() {
                int i = 1;
    
                public Thread newThread(Runnable r) {
                    return new Thread(r, "自定义线程" + i++);
                }
            });
            for (int i = 1; i <= 10; i++) {
                //提交任务
                executorService.submit(new MyRunnable0202(i));
            }
        }
    }
    
    class MyRunnable0202 implements Runnable {
        private int id;
    
        public MyRunnable0202(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "执行任务" + id);
        }
    }
    
    newSingleThreadExecutor

    测试代码

    package com.test.demo02;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadFactory;
    
    /**
     * 通过Executors获取ExecutorService
     * newSingleThreadExecutor
     * 特点:线程池只有一个线程执行任务
     *
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTest03 {
    
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            for (int i = 1; i <= 10; i++) {
                //提交任务
                executorService.submit(new MyRunnable0202(i));
            }
        }
    }
    
    /**
     * 任务类
     */
    class MyRunnable03 implements Runnable {
        private int id;
    
        public MyRunnable03(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "执行任务" + id);
        }
    }
    
    newSingleThreadExecutor(ThreadFactory threadFactory)

    测试代码

    package com.test.demo02;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadFactory;
    
    /**
     * 通过Executors获取ExecutorService
     * newSingleThreadExecutor(ThreadFactory threadFactory)
     *
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTest0303 {
    
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
                int i = 1;
    
                public Thread newThread(Runnable r) {
                    return new Thread(r, "自定义线程" + i++);
                }
            });
            for (int i = 1; i <= 10; i++) {
                //提交任务
                executorService.submit(new MyRunnable0202(i));
            }
        }
    }
    
    /**
     * 任务类
     */
    class MyRunnable0303 implements Runnable {
        private int id;
    
        public MyRunnable0303(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "执行任务" + id);
        }
    }
    
    shutdown

    测试代码

    package com.test.demo02;
    
    import java.util.List;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 通过Executors获取ExecutorService
     * 测试shutdown和shutdownNow
     * shutdown:仅仅是不再接受新任务,已经提交的任务依然会继续执行
     * shutdownNow:立刻关闭。如果线程中的任务未执行完毕,则取消这些任务并返回,保存到一个集合中
     *
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTest04 {
    
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            for (int i = 1; i <= 10; i++) {
                //提交任务
                executorService.submit(new MyRunnable0202(i));
            }
            //关闭线程池,仅仅是不再接受新任务,已经提交的任务依然会继续执行
            executorService.shutdown();
        }
    }
    
    /**
     * 任务类
     */
    class MyRunnable04 implements Runnable {
        private int id;
    
        public MyRunnable04(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "执行任务" + id);
        }
    }
    
    shutdownNow

    测试代码

    package com.test.demo02;
    
    import java.util.List;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 通过Executors获取ExecutorService
     * 测试shutdown和shutdownNow
     * shutdown:仅仅是不再接受新任务,已经提交的任务依然会继续执行
     * shutdownNow:立刻关闭。如果线程中的任务未执行完毕,则取消这些任务并返回,保存到一个集合中
     *
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTest04 {
    
        public static void main(String[] args) {
            //创建线程池对象
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            for (int i = 1; i <= 10; i++) {
                //提交任务
                executorService.submit(new MyRunnable0202(i));
            }
            //立刻关闭。如果线程中的任务未执行完毕,则取消这些任务并返回,保存到一个集合中
            List<Runnable> runnables = executorService.shutdownNow();
        }
    }
    
    /**
     * 任务类
     */
    class MyRunnable04 implements Runnable {
        private int id;
    
        public MyRunnable04(int id) {
            this.id = id;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + "执行任务" + id);
        }
    }
    
    ScheduledExecutorService

    ScheduledExecutorService是ExecutorService的子接口,具备了延迟运行或定期执行任务的能力,
    常用获取方式如下:
    static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    创建一个可重用固定线程数的线程池且允许延迟运行或定期执行任务;
    static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
    创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建,且允许延迟运行或定期执行任务;
    static ScheduledExecutorService newSingleThreadScheduledExecutor()
    创建一个单线程执行程序,它允许在给定延迟后运行命令或者定期地执行。
    static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
    创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。

    ScheduledExecutorService常用方法如下:
    ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit)
    延迟时间单位是unit,数量是delay的时间后执行callable。
    ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) 延迟时间单位是unit,数量是delay的时间后执行command。 ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
    延迟时间单位是unit,数量是initialDelay的时间后,每间隔period时间重复执行一次command。
    ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
    创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。

    注意:

    scheduleAtFixedRate:是每次执行开始的时间间隔的是固定的

    scheduleWithFixedDelay:第一次任务执行完成之后,到第二次任务开始,二者之间的时间间隔是固定的

    newScheduledThreadPool

    测试代码

        public static void main(String[] args) {
            //获取延迟执行任务的线程池对象
            ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
            for (int i = 1; i <= 10; i++) {
                //延迟2秒 执行任务
                ses.schedule(new MyRunnable(i),2, TimeUnit.SECONDS);
            }
            System.out.println("over");
        }
    
    scheduleAtFixedRate

    测试代码

        public static void main(String[] args) {
            //获取延迟执行任务的线程池对象
            ScheduledExecutorService ses = Executors.newScheduledThreadPool(3, new ThreadFactory() {
                public Thread newThread(Runnable r) {
                    int n = 1;
                    return new Thread(r, "自定义线程" + n++);
                }
            });
            /*
            initialDelay:初始延迟时间;先延迟多长事件
            period:每间隔两秒执行一次,任务执行所需要的时间包含在内,不考虑任务执行所需时间,就定期执行
             */
            ses.scheduleAtFixedRate(new MyRunnable(1), 1, 2, TimeUnit.SECONDS);
        }
    
    newSingleThreadScheduledExecutor

    测试代码

        public static void main(String[] args) {
            //获取延迟执行任务的线程池对象
            ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
                public Thread newThread(Runnable r) {
                    int n = 1;
                    return new Thread(r, "自定义线程" + n++);
                }
            });
            /*
            initialDelay:初始延迟时间
            delay:上个任务执行完成之后,到下个任务开始执行之间的时间间隔
                举例:比如执行任务需要:2秒,则当2秒钟执行完毕之后再间隔delay时间之后,才开始下次执行
             */
            ses.scheduleWithFixedDelay(new MyRunnable(1), 1, 2, TimeUnit.SECONDS);
        }
    
    

    schedule:只会执行一次

    scheduleAtFixedRate:每次任务开始间隔的时间

    scheduleWithFixedDelay:第一次任务结束之后,到第二次开始之间的时间间隔

    异步计算结果(Future)

    ExecutorService执行submit方法时,参数为Runnable或者Callable时会有返回值,就表示异步计算结果

    Future submit(Callable task) 执行带返回值的任务,返回一个Future对象。

    当我们使用多线程去计算某个任务,但是需要等待该任务的计算结果,才可继续执行。

    开发中,我们有时需要利用线程进行一些计算,然后获取这些计算的结果,而java中的Future接口就是专门用于描述异步计算结果的,我们可以通过Future 对象获取线程计算的结果;
    Future 的常用方法如下:
    boolean cancel(boolean mayInterruptIfRunning)
    试图取消对此任务的执行。
    V get()
    如有必要,等待计算完成,然后获取其结果。
    V get(long timeout, TimeUnit unit)
    如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
    boolean isCancelled()
    如果在任务正常完成前将其取消,则返回 true。
    boolean isDone()
    如果任务已完成,则返回 true。

    cancel:

    取消失败场景:任务已经执行完毕

    Runnable中的run方法没有返回值,所以没有太大意义,所以后续以Callable为主

    测试代码

    package com.test.demo04;
    
    import java.util.concurrent.*;
    
    /**
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class FutureDemo {
    
        public static void main(String[] args) throws Exception {
            //获取线程池对象
            ExecutorService executorService = Executors.newCachedThreadPool();
            Future<Integer> future = executorService.submit(new MyCallable(1, 2));
            //取消任务
            //boolean cancel = future.cancel(true);
            //System.out.println("是否取消成功:" + cancel);
    
            /*
            指定等待时间,而不是无限制等待
            TimeoutException异常:任务需要2秒钟才能执行完成,只等待1秒钟,无法执行完成,会出异常
             */
            Integer res = future.get(1, TimeUnit.SECONDS);
            System.out.println("执行结果:" + res);
        }
    
        /**
         * 正常测试流程
         *
         * @param future
         * @throws Exception
         */
        private static void method1(Future<Integer> future) throws Exception {
            //任务是否完成
            boolean done = future.isDone();
            System.out.println("任务是否完成:" + done);
    
            //任务是否取消
            boolean cancelled = future.isCancelled();
            System.out.println("任务是否取消:" + cancelled);
    
            //任务执行结果 一直等待任务的执行时间 直到任务完成
            Integer res = future.get();
            System.out.println("任务执行的结果是:" + res);
    
            //任务是否完成
            boolean done2 = future.isDone();
            System.out.println("任务是否完成:" + done2);
        }
    
    }
    
    class MyCallable implements Callable<Integer> {
    
        private int a;
    
        private int b;
    
        public MyCallable(int a, int b) {
            this.a = a;
            this.b = b;
        }
    
        public Integer call() throws InterruptedException {
            String threadName = Thread.currentThread().getName();
            System.out.println("开始计算");
            Thread.sleep(2000);
            System.out.println("计算结束");
            return a + b;
        }
    }
    
    

    综合案例-秒杀商品

    案例介绍:
    假如某网上商城推出活动,新上架10部新手机免费送客户体验,要求所有参与活动的人员在规定的时间同时参与秒杀挣抢,假如有20人同时参与了该活动,请使用线程池模拟这个场景,保证前10人秒杀成功,后10人秒杀失败;
    要求:
    1:使用线程池创建线程
    2:解决线程安全问题
    思路提示:
    1:既然商品总数量是10个,那么我们可以在创建线程池的时候初始化线程数是10个及以下,设计线程池最大数量为10个;
    2:当某个线程执行完任务之后,可以让其他秒杀的人继续使用该线程参与秒杀;
    3:使用synchronized控制线程安全,防止出现错误数据;
    代码步骤:
    1:编写任务类,主要是送出手机给秒杀成功的客户;
    2:编写主程序类,创建20个任务(模拟20个客户);
    3:创建线程池对象并接收20个任务,开始执行任务;

    代码

    任务类

    package com.test.demo05;
    
    /**
     * 任务类
     *
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTask implements Runnable {
    
        /**
         * static:共享
         * 商品数量:10
         */
        private static int count = 10;
    
        /**
         * 客户名称
         */
        private String userName;
    
        public MyTask(String userName) {
            this.userName = userName;
        }
    
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(userName + "正在使用" + threadName + "参与秒杀任务...");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (MyTask.class) {
                if (count > 0) {
                    System.out.println(userName + "使用" + threadName + "秒杀" + count-- + "号商品成功");
                } else {
                    System.out.println(userName + "使用" + threadName + "秒杀商品失败");
                }
            }
        }
    }
    

    测试类

    package com.test.demo05;
    
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTest {
    
        public static void main(String[] args) {
            //线程池
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<Runnable>(15));
            //创建任务
            for (int i = 1; i <= 20; i++) {
                threadPoolExecutor.submit(new MyTask("客户" + i));
            }
            //关闭
            threadPoolExecutor.shutdown();
        }
    }
    

    综合案例-取款业务

    案例介绍:
    设计一个程序,使用两个线程模拟在两个地点同时从一个账号中取钱,假如卡中一共有1000元,每个线程取800元,要求演示结果一个线程取款成功,剩余200元,另一个线程取款失败,余额不足;
    要求:
    1:使用线程池创建线程
    2:解决线程安全问题

    ​ 思路提示:
    ​ 1:线程池可以利用Executors工厂类的静态方法,创建线程池对象;
    ​ 2:解决线程安全问题可以使用synchronized方法控制取钱的操作
    ​ 3:在取款前,先判断余额是否足够,且保证余额判断和取钱行为的原子性;
    代码

    package com.test.demo06;
    
    /**
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTask implements Runnable {
    
        /**
         * 用户名
         */
        private String userName;
    
        /**
         * 取款金额
         */
        private double money;
    
        /**
         * 总金额
         */
        private static double total = 1000;
    
        public MyTask(String userName, double money) {
            this.userName = userName;
            this.money = money;
        }
    
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(userName + "正在使用" + threadName + "取款" + money);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (MyTask.class) {
                if (total - money > 0) {
                    //取款成功
                    System.out.println(userName + "使用" + threadName + "取款" + money + "成功,余额" + (total - money));
                    //取款
                    total -= money;
                } else {
                    System.out.println(userName + "使用" + threadName + "取款" + money + "失败");
                }
            }
        }
    }
    

    测试代码

    package com.test.demo06;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadFactory;
    
    /**
     * @author zhangzengxiu
     * @date 2022/9/27
     */
    public class MyTest {
    
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newFixedThreadPool(2, new ThreadFactory() {
                int i = 1;
    
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "ATM" + i++);
                }
            });
    
            for (int i = 1; i <= 2; i++) {
                executorService.submit(new MyTask("客户" + i, 800));
            }
            //关闭线程池
            executorService.shutdown();
        }
    }
    

    总结

    线程池的使用步骤可以归纳总结为五步 :
    1:利用Executors工厂类的静态方法,创建线程池对象;
    2:编写Runnable或Callable实现类的实例对象;
    3:利用ExecutorService的submit方法或ScheduledExecutorService的schedule方法提交并执行线程任务
    4:如果有执行结果,则处理异步执行结果(Future)
    5:调用shutdown()方法,关闭线程池

  • 相关阅读:
    什么是前端框架中的数据绑定(data binding)?有哪些类型的数据绑定?
    基于 ARM+FPGA+AD平台的多类型同步信号采集仪开发及试验验证(二)板卡总体设计
    SpringBoot配置文件
    vue 公众号h5分享功能 监听微信右上角...
    易点易动:提升企业固定资产管理效率的完美解决方案
    解决拦截器抛出异常处理类的500状态码Html默认格式响应 !
    双非二本上岸滴滴暑期实习总结
    60页5G+智慧农业大数据 2022
    【情态动词练习题】will 与 would
    Python概率-电网-线性和非线性方程和数据分析工程应用
  • 原文地址:https://blog.csdn.net/zhangzengxiu/article/details/126963997