其他分享
首页 > 其他分享> > 线程池用过吗?ThreadPoolExecutor谈谈你的理解

线程池用过吗?ThreadPoolExecutor谈谈你的理解

作者:互联网

线程池的主要工作是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了池最大数量,超出的线程排队等候,等其他线程执行完毕,再从队列取出任务来执行。

它的主要特点为:线程复用控制最大并发数管理线程

为什么要用线程池,优势:

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统稳定性,使用线程池可以进行统一分配,调优和监控

线程池如何使用:

线程池的几个重要参数介绍:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}

说说线程池的底层工作原理:

  1. 创建线程池后,等待提交过来的任务请求
  2. 当调用execute()方法添加一个请求任务时,线程会作如下判断
    2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务
    2.2 如果正在运行的线程数量大于或等于corePoolSize,那么这个任务将进入等待队列
    2.3 如果这时队列满了且正在运行的线程数量还是小于maximumPoolSize,那么还是要创建非核心线程立即运行这个任务
    2.4 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行
  3. 当一个线程完成任务时,他会从队列中取下一个任务来执行
  4. 当一个线程无事可做超过一定的时间keepAliveTime时,线程池会判断:
    如果当前运行的线程数量超过corePoolSize,那么这个线程就会被停掉
    所以线程池的所有任务完成后最终会收缩到corePoolSize的大小




第三种获得多线程的方法:
Callable 接口
如何使用Callable创建一个线程: 通过FutureTask的构造方法
在这里插入图片描述
模拟做菜准备:买厨具、买菜。厨具是网购,买菜是自己去

public static void main(String[] args) {
    long start = System.currentTimeMillis();

    Thread tool = new Thread(() -> {
        try {
            System.out.println(Thread.currentThread().getName() + " 购买厨具");
            System.out.println(Thread.currentThread().getName() + " 厨具开始送货");
            TimeUnit.SECONDS.sleep(3);
            System.out.println(Thread.currentThread().getName() + " 厨具送到");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "toolThread");

    tool.start();

    //模拟等待送货时间,因为必须要得到厨具才能做饭,而送货时间是不确定的,送货线程也没有返回值,我们无法知道该什么时候去获取厨具,所以一直等他的结果
    try {
        tool.join();
        System.out.println("获得厨具:"+ "厨具");
        System.out.println("shopThread开始去买菜");
        //买菜时间
        TimeUnit.SECONDS.sleep(3);
        System.out.println("shopThread买菜完毕");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("准备工作完毕");

    long end = System.currentTimeMillis();
    System.out.println("共用时:" + (end-start) / 1000 + "s");
}


toolThread 购买厨具
toolThread 厨具开始送货
toolThread 厨具送到
获得厨具:厨具
shopThread开始去买菜
shopThread买菜完毕
准备工作完毕
共用时:6s

这两个过程应该可以同时分开进行

public static void main(String[] args) {
    long start = System.currentTimeMillis();

    FutureTask<String> toolTask = new FutureTask(() -> {
        try {
            System.out.println(Thread.currentThread().getName() + " 购买厨具");
            System.out.println(Thread.currentThread().getName() + " 厨具开始送货");
            TimeUnit.SECONDS.sleep(3);
            System.out.println(Thread.currentThread().getName() + " 厨具送到");
            return "厨具";
        } catch (InterruptedException e) {
            throw new InterruptedException();
        }
    });

    new Thread(toolTask, "toolThread").start();

    try {
        System.out.println("shopThread开始去买菜");
        //买菜时间
        TimeUnit.SECONDS.sleep(3);
        System.out.println("shopThread买菜完毕");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    try {
        String tool = toolTask.get(); //获取异步线程工作的返回值,如果还没执行完会造成阻塞,所以尽量放在最后
        //可以用 toolTask.isDOne() 判断是否执行完成
        System.out.println("获得厨具:" + tool);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    System.out.println("准备工作完毕");

    long end = System.currentTimeMillis();
    System.out.println("共用时:" + (end-start) / 1000 + "s");

}


shopThread开始去买菜
toolThread 购买厨具
toolThread 厨具开始送货
shopThread买菜完毕
toolThread 厨具送到
获得厨具:厨具
准备工作完毕
共用时:3s

拥有相同FutureTask的线程启动,只会运行一次,它的结果可以复用

new Thread(toolTask, "AA").start();
new Thread(toolTask, "BB").start();


第四种获得多线程的方法,线程池:

Java中的线程池是通过Executor框架实现的,该框架中用到了Executor, Executors. ExecutorService, ThreadPoolExecutor 这几个类在这里插入图片描述

编码实现:

public static void main(String[] args) {
    ExecutorService threadPool = Executors.newFixedThreadPool(3);

    try {
        //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
        for (int i = 0; i < 6 ; i++) {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " doing job");
                try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPool.shutdown();
    }
}


pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
public static void main(String[] args) {
    ExecutorService threadPool = Executors.newSingleThreadExecutor();

    try {
        for (int i = 0; i < 6 ; i++) {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " doing job");
                try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPool.shutdown();
    }
}


pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
public static void main(String[] args) {
    ExecutorService threadPool = Executors.newCachedThreadPool();

    try {
        for (int i = 0; i < 6 ; i++) {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " doing job");
                try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPool.shutdown();
    }
}


pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
pool-1-thread-4 doing job
pool-1-thread-5 doing job
pool-1-thread-6 doing job

这三个线程池构建方法的底层: ThreadPoolExecutor

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}    

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}    
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {}

标签:doing,池用,System,厨具,job,线程,println,ThreadPoolExecutor
来源: https://blog.csdn.net/sakuragio/article/details/100656399