线程实现以及相关异步处理

1.线程的实现方式

1.线程实现

1.1继承Thread

public class ThreadDemo {
    public static void main(String[] args) {
        ThreadExtends threadExtends = new ThreadExtends();
        threadExtends.start();
    }
}

class ThreadExtends extends Thread{
    @Override
    public void run() {
        System.out.println("当前线程=="+Thread.currentThread().getName());
    }
}

1.2实现Runnable接口

public class RunnableDemo {
    public static void main(String[] args) {
        RunnableDemo01 runnableDemo01 = new RunnableDemo01();
        new Thread(runnableDemo01).start();
        /*new Thread(()->{
            System.out.println("当前线程:=="+Thread.currentThread().getName());
        }).start();*/
    }
}

class RunnableDemo01 implements Runnable{
    @Override
    public void run() {
        System.out.println("当前线程:=="+Thread.currentThread().getName());
    }
}

1.3实现callable

public class CallableDemo {
    public static void main(String[] args) throws Exception {
        CallableDemo01 callableDemo01 = new CallableDemo01();
        FutureTask task = new FutureTask(callableDemo01);
        new Thread(task).start();
        Object o = task.get();
        System.out.println("o = " + o);
    }
}

class CallableDemo01 implements Callable<Object>{
    @Override
    public Object call() throws Exception {
        System.out.println("当前线程:=="+Thread.currentThread().getName());
        return 12;
    }
}

2.线程池实现

详解请看下面线程池

3.获取线程的区别

通过上面的介绍我们发现获取线程的方式

  • 继承Thread对象
  • 实现Runnable接口
  • 实现Callable接口
  • 线程池

继承Thread对象和实现Runnable接口没有办法获取返回结果的,实现Callable接口可以获取线程的返回结果。当然这三种方式都不能控制我们的资源,线程池可以控制资源。

4.线程的几种状态

在这里插入图片描述

线程共包括以下 5 种状态:

  1. 新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。

  2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。

  3. 运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。

  4. 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

  • (01) 等待阻塞 – 通过调用线程的wait()方法,让线程等待某工作的完成。
  • (02) 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
  • (03) 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  1. 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

2.线程池详解

1.线程池参数介绍

在这里插入图片描述

  1. corePoolSize 线程池核心线程大小

线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务

  1. maximumPoolSize 线程池最大线程数量

当前线程数达到corePoolSize后,如果继续有任务被提交到线程池,会将任务缓存到工作队列中。如果队列也已满,则会去创建一个新线程来。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定

3.keepAliveTime 空闲线程存活时间

一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定

  1. unit 空闲线程存活时间单位

keepAliveTime的计量单位
TimeUnit是一个枚举类型:
NANOSECONDS : 1微毫秒,
MICROSECONDS:1微秒
MILLSECONDS:1毫秒
SECONDS:1秒
MINUTES:1分
HOURS:1小时
DAYS:1天

5.workQueue 工作队列

新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

  • ①ArrayBlockingQueue
    基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
  • ②LinkedBlockingQuene
    基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
  • ③SynchronousQuene
    一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
  • ④PriorityBlockingQueue
    具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

6.threadFactory 线程工厂

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

  1. handler 拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:

  • ①CallerRunsPolicy
    该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
    在这里插入图片描述
  • ②AbortPolicy
    该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
    在这里插入图片描述
  • ③DiscardPolicy
    该策略下,直接丢弃任务,什么都不做。
    在这里插入图片描述
  • ④DiscardOldestPolicy
    该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
    在这里插入图片描述

2.常用的四种线程池

1.newCachedThreadPool (可缓存线程池)

在这里插入图片描述
核心线程数为0,总线程数是最大整数,当需要执行很多短时任务时它的使用率比较高,会显著提升性能。并且线程空闲60s就会被回收,所以如果没有任务,它并不会占用很多资源。

2.newFixedThreadPool(指定工作线程数量的线程池)

在这里插入图片描述
核心线程数等于最大线程数。也就是该线程池中没有非核心线程。

3.newSingleThreadPool(单线程化的线程池)

在这里插入图片描述
只有一个核心线程,所有任务按照先来先执行的顺序执行。

4.newScheduledThreadPool(定长线程池)

在这里插入图片描述
这个线程池指定了核心线程的数量,线程总数可以创建整数的最大数个。
该线程池支持定时以及周期性任务执行。

3.线程池执行顺序

在这里插入图片描述

4. 线程池核心数与最大线程数设置

核心线程
CPU密集型:核心线程数=CPU核心数(或 核心线程数=CPU核心数+1)
I/O密集型:核心线程数=2*CPU核心数(或 核心线程数=CPU核心数/(1-阻塞系数))
混合型:核心线程数=(线程等待时间/线程CPU时间+1)*CPU核心数

最大线程
IO密集型经验应用,最大线程设置为 2N+1 (N为CPU数量,下同)
CPU密集型应用,最大线程设置为 N+1

3.异步处理

1.ComplatableFuture介绍

  Future是Java 5添加的类,用来描述一个异步计算的结果。你可以使用** isDone方法检查计算是否完成,或者使用 get阻塞住调用线程,直到计算完成返回结果,你也可以使用 cancel方法停止任务的执行。
  虽然 Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?
  在Java 8中, 新增加了一个包含50个方法左右的类: CompletableFuture,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。
  CompletableFuture类实现了Future接口,所以你还是可以像以前一样通过 get方法阻塞或者轮询的方式获得结果,但是这种方式不推荐使用。
  CompletableFuture和FutureTask同属于Future接口的实现类,都可以获取线程的执行结果。
在这里插入图片描述

2.创建异步对象

CompletableFuture 提供了四个静态方法来创建一个异步操作

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

方法分为两类:

  • runAsync 没有返回结果
  • supplyAsync 有返回结果
public class CompletableFutureDemo01 {

    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5,
            10,
            10L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) throws Exception{
        CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
            Integer a = 100/50;
            System.out.println("run当前线程:=="+Thread.currentThread().getName());
        }, executor);

        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
            Integer a = 100 / 2;
            System.out.println("supply当前线程:=="+Thread.currentThread().getName());
            return a;
        }, executor);
        System.out.println("线程返回值:"+supplyAsync.get());
    }

}

3.处理异常

 当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。主要是下面的方法:

public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor);

public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn);

public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) ;
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) ;
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) ;

相关方法的说明:

  • whenComplete 可以获取异步任务的返回值和抛出的异常信息,但是不能修改返回结果
  • execptionlly 当异步任务跑出了异常后会触发的方法,如果没有抛出异常该方法不会执行
  • handle 可以获取异步任务的返回值和抛出的异常信息,而且可以显示的修改返回的结果
public class CompletableFutureDemo02 {
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5,
            10,
            10L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) throws Exception{
        /*CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(()->{
            Integer a = 100/20;
            return a;
        },executor).whenCompleteAsync((res,exc)->{
            System.out.println("res:"+res);
            System.out.println("exc:"+exc);
        }).exceptionally(res->{//在异步任务显示的抛出了异常后才会触发的方法,没有异常不执行
            System.out.println("res:"+res);
            return 10;
        });
        System.out.println("返回结果:"+supplyAsync.get());*/

        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
            Integer a = 100 / 25;
            return a;
        }, executor).handleAsync((res,exc)->{//没有错误抛出,却能修改最终的返回结果
            System.out.println("res:"+res);
            System.out.println("exc:"+exc);
            return res * 10;
        });
        System.out.println("结果:"+supplyAsync.get());
    }
}

4. 线程串行

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);

thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行 thenRun的后续操作
带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。

public class CompletableFutureDemo03 {
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5,
            10,
            10L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) throws Exception{
        /*CompletableFuture<Integer> thenApply = CompletableFuture.supplyAsync(() -> {
            Integer a = 20 / 10;
            return a;
        }, executor).thenApply(res -> {
            return res * 5;
        });
        System.out.println("返回值:"+thenApply.get());*/
        /*CompletableFuture<Void> thenAccept = CompletableFuture.supplyAsync(() -> {
            Integer a = 100 / 4;
            return a;
        }, executor).thenAccept(res -> {
            System.out.println("res:" + res * 10);
        });*/
        CompletableFuture<Void> thenRun = CompletableFuture.supplyAsync(() -> {
            Integer a = 100 / 50;
            return a;
        }, executor).thenRunAsync(()-> {
            Integer b = 200/4;
            System.out.println("b"+b);
        }, executor);
    }
}

5.两个都完成

上面介绍的相关方法都是串行的执行,接下来看看需要等待两个任务执行完成后才会触发的几个方法

  • thenCombine :可以获取前面两线程的返回结果,本身也有返回结果
  • thenAcceptBoth:可以获取前面两线程的返回结果,本身没有返回结果
  • runAfterBoth:不可以获取前面两线程的返回结果,本身也没有返回结果
public class CompletableFutureDemo04 {
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5,
            10,
            10L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) throws Exception{
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            Integer a = 10;
            return a;
        },executor);

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            Integer b = 2;
            return b;
        }, executor);
        // thenCombineAsync: 既可以获取前面两个线程的返回结果,同时也会返回结果给阻塞的线程
        CompletableFuture<Integer> thenCombineAsync = future1.thenCombineAsync(future2, (f1, f2) -> {
            return f1 + f2;
        }, executor);

        System.out.println("结果:"+thenCombineAsync.get());

        // thenAcceptBothAsync 可以获取前面两个线程的返回结果,本身没有返回结果
        CompletableFuture<Void> acceptBothAsync = future1.thenAcceptBothAsync(future2, (f1, f2) -> {
            System.out.println("both:"+(f1+f2));
        }, executor);

        // runAfterBothAsync 不能获取前面两个线程的返回结果,本身也没有返回结果
        CompletableFuture<Void> afterBothAsync = future1.runAfterBothAsync(future2, () -> {
            System.out.println("afterBothAsync执行");
        }, executor);
    }
}

6.两个任务完成一个

两个任务只要有一个完成就会触发任务3的情况

  • runAfterEither:不能获取完成的线程的返回结果,自身也没有返回结果
  • acceptEither:可以获取线程的返回结果,自身没有返回结果
  • applyToEither:既可以获取线程的返回结果,自身也有返回结果
public class CompletableFutureDemo05 {
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5,
            10,
            10L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) throws Exception{
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1开始了..."+Thread.currentThread().getName());
            Integer a = 10 * 2;
            System.out.println("任务1结束了..."+Thread.currentThread().getName());
            return a;
        }, executor);

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2开始了..."+Thread.currentThread().getName());
            Integer b = 100/4;
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2结束了..."+Thread.currentThread().getName());
            return b;
        }, executor);

        // runAfterEitherAsync 不能获取前面完成的线程的返回结果,自身也没有返回结果
        future1.runAfterEitherAsync(future2,()->{
            System.out.println("任务3开始了...");
        },executor);

        //可以获取线程的返回结果,自身没有返回结果
        future1.acceptEitherAsync(future2,res->{
            System.out.println("res:"+res);
            System.out.println("任务3开始了...");
        },executor);

        //既可以获取线程的返回结果,自身也有返回结果
        CompletableFuture<Integer> toEitherAsync = future1.applyToEitherAsync(future2, res -> {
            Integer c = res * 10;
            System.out.println("任务3开始了...");
            return c;
        }, executor);

        System.out.println("结果:"+toEitherAsync.get());
    }
}

7.多任务组合

allOf:等待所有任务完成
anyOf:只要有一个任务完成

public class CompletableFutureDemo06 {

    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5,
            10,
            10L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) throws Exception{

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1 线程开始了..." + Thread.currentThread().getName());
            int a = 100 / 5;
            System.out.println("任务1 线程结束了..." + Thread.currentThread().getName());
            return a;
        }, executor);

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2 线程开始了..." + Thread.currentThread().getName());
            int b = 100 /10;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2 线程结束了..." + Thread.currentThread().getName());
            return b;
        }, executor);

        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务3 线程开始了..." + Thread.currentThread().getName());
            int c = 100 /1;
            System.out.println("任务3 线程结束了..." + Thread.currentThread().getName());
            return c;
        }, executor);

        CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2, future3);
        //阻塞,等待所有任务完成
        allOf.get();
        System.out.println("主任务执行完成..." + future1.get() + " :" + future2.get() + " :" + future3.get());

        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2, future3);
        System.out.println("主任务执行完成..." + anyOf.get());
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CompletableFuture 是 Java 8 引入的一个类,用于实现线程异步编程。它提供了丰富的方法来处理异步任务的结果、组合多个异步任务以及处理异常情况。 要实现超时处理,你可以结合 CompletableFuture 的方法和 Java 的 ScheduledExecutorService 来实现。下面是一个示例: ```java import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class CompletableFutureTimeoutExample { public static void main(String[] args) { // 创建一个 ScheduledExecutorService 用于超时处理 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); // 创建一个 CompletableFuture 对象 CompletableFuture<String> future = new CompletableFuture<>(); // 设置超时时间 long timeout = 5000; // 5秒 // 在指定时间后取消 CompletableFuture executorService.schedule(() -> { if (!future.isDone()) { future.completeExceptionally(new TimeoutException("Timeout")); } }, timeout, TimeUnit.MILLISECONDS); // 异步任务 CompletableFuture.runAsync(() -> { try { // 执行异步操作,例如网络请求、数据库查询等 String result = performAsyncTask(); // 当任务完成时,设置结果给 CompletableFuture future.complete(result); } catch (Exception e) { future.completeExceptionally(e); } }); // 获取结果或处理超时异常 try { String result = future.get(); // 处理成功结果 } catch (TimeoutException e) { // 处理超时异常 } catch (InterruptedException | ExecutionException e) { // 处理其他异常 } // 关闭 ScheduledExecutorService executorService.shutdown(); } private static String performAsyncTask() { // 执行异步任务的逻辑 return "Async task result"; } } ``` 在上面的示例中,我们创建了一个 CompletableFuture 对象,并使用 ScheduledExecutorService 在指定的超时时间后取消 CompletableFuture。然后,我们使用 CompletableFuture.runAsync 方法执行异步任务,并在任务完成时设置结果给 CompletableFuture。最后,我们使用 future.get() 方法来获取结果或处理超时异常。 希望以上信息能够帮助到你!如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值