异步与线程池

igxiaoshan Lv5

线程创建的方式

  • 第一种

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 通过继承 thread 类,重写 run(),创建对象实例,调用start()方法启动线程

    public class ThreadTest {
    public static void main(String[] args) {
    MyThread myThread = new MyThread();
    myThread.start();
    }

    public static class MyThread extends Thread {
    @Override
    public void run() {
    System.out.println("线程执行了" + Thread.currentThread().getId());
    }
    }
    }

  • 第二种

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 实现 Runnable 接口,重写run(),创建对象实例,使用Thread线程启动线程

    public class ThreadTest {
    public static void main(String[] args) {
    MyRunnable myRunnable = new MyRunnable();
    new Thread(myRunnable).start();
    }

    public static class MyRunnable implements Runnable{
    @Override
    public void run() {
    System.out.println("线程执行了" + Thread.currentThread().getId());
    }
    }
    }
  • 第三种

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 实现 Callable<T> 接口,重写call(),使用FurureTask对象启动线程
    // Callable 它是异步执行,调用 get(),会阻塞等待,直到线程结束

    public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<Integer> task = new FutureTask<>(new MyCallable());
    new Thread(task).start();
    Integer value = task.get();
    System.out.println(value);
    }

    public static class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
    System.out.println("线程执行了" + Thread.currentThread().getId());
    Integer num = 10 / 2;
    return num;
    }
    }
    }

  • 第四种:**线程池**

    1
    2
    3
    4
    5
    6
    // 在高并发,多线程的程序开发中,持续的创建单个线程,很消耗资源
    // 所以,在一个系统中,会维护一个线程池,有线程任务交给线程池去执行.

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    executor.submit(new runnable());

总结:

​ 第一种和第二种创建线程,不能得到返回值,

​ 第三种可以获取返回值

​ 第一,二,三种方式,均不能控制资源

​ 第四种可以控制资源

线程池

线程池7大参数解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 构造一
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}


// 构造二
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
  • corePoolSize :线程池核心线程数量,即使是线程处于空闲状态也不会销毁,除非设置了allowCoreThreadTimeOut。也是最小线程数量
  • maximumPoolSize :线程池最大线程数量
    • 一个任务被提交到线程池后,首先会找有没有空闲的存活的线程,如果有则直接将任务交给空闲的线程来执行;如果没有则会缓存到工作队列种,如果工作队列满了,才会创建一个新的线程,然后从工作队列的头部取出一个任务交由新线程来执行。而刚刚提交的任务放入队列的尾部。线程池不会无限制的去创建新线程,他会有一个最大线程数量的限制,这个数量就是有 maximumPoolSize 来指定的。
  • keepAliveTime :当线程数大于核心时,这是多余的空闲线程在终止前等待新任务的最长时间。
  • unit :keepAliveTime 参数的时间单位
  • workQueue:阻塞队列,用来存储等待执行的任务。如果当前对线程的需求超过了 corePoolSize 指定的大小,就会放在这里等待空闲线程执行。
  • threadFactory:线程的创建工厂
  • handler:如果队列满了,按照指定的拒绝策略,拒绝执行任务。

工作流程

  • 线程池创建,准备好 core 数量的核心线程,准备接受任务
  • 新的任务进来,用 core 准备好的空闲线程执行。
    • core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
    • 阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
    • max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
    • 如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
  • 所有的线程创建都是由指定的 factory 创建的

案例

一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的

  • 先使用核心线程7个执行任务,多余任务进入队列,再开13个线程继续执行,最大开到20个.剩余30个会被默认策略拒绝

常见的4种线程池

  • newCachedThreadPool
    • 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
  • newFixedThreadPool
    • 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
  • newScheduledThreadPool
    • 创建一个定长线程池,支持定时及周期性任务执行
  • newSingleThreadExecutor
    • 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

开发中线程池的优势

  • 降低资源的消耗
    • 通过重复利用已经创建好的线程降低线程的创建和销毁带来的消耗
  • 提高响应速度
    • 因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
  • 提高线程的可管理性
    • 线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销.无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配