异步&线程池

1、初始化线程的 4 种方式

1)继承 Thread

package com.search.thread;

import java.util.concurrent.*;

public class ThreadTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
         System.out.println("main......start.....");
         Thread thread = new Thread01();
         thread.start();
         System.out.println("main......end.....");
     }
     
    public static class Thread01 extends Thread {
        @Override
        public void run() {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
        }
    }
}

2)实现 Runnable 接口

package com.search.thread;

import java.util.concurrent.*;

public class ThreadTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
         System.out.println("main......start.....");
         Runable01 runable01 = new Runable01();
         new Thread(runable01).start();
         System.out.println("main......end.....");
     }

     public static class Runable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
        }
    }
}

3)实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)

package com.search.thread;

import java.util.concurrent.*;

public class ThreadTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
         System.out.println("main......start.....");
         FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
         new Thread(futureTask).start();
         //阻塞等待整个线程执行完成,获取返回结果
         System.out.println(futureTask.get());
         System.out.println("main......end.....");
     }

     public static class Callable01 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }
    }
}

4)线程池

package com.search.thread;

import java.util.concurrent.*;

/**
  给线程池直接提交任务。
    1、创建:
        service.execute(new Runable01());
        1)、Executors
        2) 、new ThreadPoolExecutor
        Future:可以获取到异步结果
     
   我们以后在业务代码里面,以上三种启动线程的方式都不用。将所有的多线程异步任务都交给线程池执行。
   当前系统中池只有一两个,每个异步任务,提交给线程池让他自己去执行就行。
**/
public class ThreadTest {

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

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
        System.out.println("main......start.....");
        // CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        //     System.out.println("当前线程:" + Thread.currentThread().getId());
        //     int i = 10 / 2;
        //     System.out.println("运行结果:" + i);
        // }, executor);

        /**
         * 方法完成后的处理
         */
        // CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        //     System.out.println("当前线程:" + Thread.currentThread().getId());
        //     int i = 10 / 0;
        //     System.out.println("运行结果:" + i);
        //     return i;
        // }, executor).whenComplete((res,exception) -> {
        //     //虽然能得到异常信息,但是没法修改返回数据
        //     System.out.println("异步任务成功完成了...结果是:" + res + "异常是:" + exception);
        // }).exceptionally(throwable -> {
        //     //可以感知异常,同时返回默认值
        //     return 10;
        // });

        /**
         * 方法执行完后端处理
         */
        // CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        //     System.out.println("当前线程:" + Thread.currentThread().getId());
        //     int i = 10 / 2;
        //     System.out.println("运行结果:" + i);
        //     return i;
        // }, executor).handle((result,thr) -> {
        //     if (result != null) {
        //         return result * 2;
        //     }
        //     if (thr != null) {
        //         System.out.println("异步任务成功完成了...结果是:" + result + "异常是:" + thr);
        //         return 0;
        //     }
        //     return 0;
        // });


        /**
         * 线程串行化
         * 1、thenRunL:不能获取上一步的执行结果
         * 2、thenAcceptAsync:能接受上一步结果,但是无返回值
         * 3、thenApplyAsync:能接受上一步结果,有返回值
         *
         */
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }, executor).thenApplyAsync(res -> {
            System.out.println("任务2启动了..." + res);
            return "Hello" + res;
        }, executor);
        System.out.println("main......end....." + future.get());

    }

    private static void threadPool() {

        ExecutorService threadPool = new ThreadPoolExecutor(
                200,
                10,
                10L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(10000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        //定时任务的线程池
        ExecutorService service = Executors.newScheduledThreadPool(2);
    } 
}

方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景
方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。
方式 4:通过如下两种方式初始化线程池

Executors.newFiexedThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory, handler);
通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一个异步调用可能会依赖于另一个异步调用的执行结果。

二、线程池7大参数

异步&线程池_第1张图片

   七大参数:
    corePoolSize:[5] 核心线程数[一直存在,除非 (allowCoreThreadTime0ut) ]; 线程池创建好以后就准备就绪的线程数量,就等待来接受异步任务去执行。
             5个 Thread thread = new Thread(); thread,start();
    maximumPoolsize:[200] 最大线程数量; 控制资源
    keepAliveTime:存活时间。如果当前的线程数量大于core数量。释放空闲的线程(maximumPoolSize-corePoolSize)。只要线程空闲大于指定的keepAliveTime;
    unit:时间单位
    BlockingQueue<Runnable> workQueue:阻塞队列。如果任务有很多,就会将目前多的任务放在队列里面,只要有线程空闲,就会去队列里面取出新的任务继续执行。
    threadFactory:线程的创建工厂。
    RejectedExecutionHandler handler;如果队列满了,按照我们指定的拒绝策略拒绝执行任务

三、线程池运行流程

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

四、面试题

一个线程池 core 7; max 2 ,queue: 5,如果100并发进来是怎么分配的?

7个会立即得到执行,50个会进入队列,再开13个进行执行。剩下的30个就使用拒绝策略.如果不想抛弃还要执行。CallerRunsPolicy;

五、常见的 4 种线程池

1.newCachedThreadPool(core是0,所有都可回收):创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若 无可回收,则新建线程。
2.newFixedThreadPool(固定大小,core=max;都不可回收):创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3. newScheduledThreadPool(定时任务的线程池):创建一个定长线程池,支持定时及周期性任务执行。
4.newSingleThreadExecutor(单线程的线程池,后台从队列里面获取任务,挨个执行):创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

六、开发中为什么使用线程池

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

七、线程池在项目中的实际应用

1.CompletableFuture 异步编排
业务场景:
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
异步&线程池_第2张图片
假如商品详情页的每个查询,需要如下标注的时间才能完成。那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。
如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应。

你可能感兴趣的:(java,开发语言)