多线程异步编程

线程的四种初始化方式

1、继承Thread类

2、实现Runnable接口

3、Callable接口

4、线程池

继承Thread类

继承Thread类复写父类中的run()。Thread类也是Runnable接口的一个子类。要想启动线程必须依靠Threan类的start()方法执行,线程启动之后会默认调用run()方法,一个线程只能启动唯一一次,多次启动会抛异常

public class ThreadDemo {

    public static void main(String[] args) {
        Thread myThread1 = new MyThread();     // 创建一个新的线程  myThread1  此线程进入新建状态
        Thread myThread2 = new MyThread();     // 创建一个新的线程 myThread2 此线程进入新建状态
        myThread1.start();                     // 调用start()方法使得线程进入就绪状态
        myThread2.start();                     // 调用start()方法使得线程进入就绪状态
    }
}

class MyThread extends Thread {

    private int i = 0;

    @Override
    public void run() {
        for (i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

实现Runable接口

​ 因为Java是单继承的,一个类只能有一个父类,所以如果已经继承了一个类就不能在继承Thread类了。所以这时候启动线程可以用实现Runnable接口来完成。

public class ThreadDemo {
    public static void main(String[] args) {
        Runnable myRunnable = new MyRunnable(); 
        new Thread(myRunnable).start();
        new Thread(myRunnable).start(); 
    }
}
class MyRunnable implements Runnable {
    private int i = 0;

    @Override
    public void run() {
        for (i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

实现Callable接口

​ 实现Callable接口相对于实现Runnable接口它有返回值,而且还可以抛出异常,执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。

public class ThreadDemo {
    public static void main(String[] args) {

        // 创建MyCallable对象
        Callable<Integer> myCallable = new MyCallable();

        //使用FutureTask来包装MyCallable对象
        FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);

        //FutureTask对象作为Thread对象的target创建新的线程
        new Thread(ft).start();

        System.out.println("主线程for循环执行完毕..");

        try {
            //取得新创建的新线程中的call()方法返回的结果
            // get方法会阻塞,一直等待call()方法执行完后才会执行。
            int sum = ft.get();
            System.out.println("sum = " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<Integer> {
    private int i = 0;
    @Override
    public Integer call() {
        int sum = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            sum += i;
        }
        return sum;
    }
}

线程池

在线程池中维护一个稳定的线程数量,有任务需要执行就到线程池中申请一个线程去执行,可以达到资源的控制。

public class ThreadDemo {
    // 创建一个线程池
    static ExecutorService executorService = Executors.newFixedThreadPool(5);
    public static void main(String[] args) throws Exception {
        
        // 提交给线程池执行一个任务
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("ThreadDemo.run");;
            }
        });
    }
}

class MyCallable implements Callable<Integer> {
    private int i = 0;

    @Override
    public Integer call() {
        int sum = 0;
        for (; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            sum += i;
        }
        return sum;
    }
}
线程池的7个核心参数
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize:线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁。线程池初始化后就已经有的线程数量。

maximumPoolSize:线程池最大线程数量

一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。

keepAliveTime:空闲线程存活时间

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

unit: 空闲线程存活时间单位

workQueue:核心线程用完了,然后把新的任务压到队列中。

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

①ArrayBlockingQueue

基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

②LinkedBlockingQuene

基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。

③SynchronousQuene

一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

④PriorityBlockingQueue

具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

threadFactory:创建一个新线程时使用的工厂,可以用来设定线程名

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

①CallerRunsPolicy

在调用者线程中直接同步执行任务的run方法,除非线程池已经shutdown,则直接抛弃任务。

②AbortPolicy

直接丢弃任务,并抛出RejectedExecutionException异常。

③DiscardPolicy

直接丢弃任务,什么都不做。

④DiscardOldestPolicy

抛弃进入队列最早的那个任务(抛弃队列的头部任务),然后尝试把这次拒绝的任务放入队列

ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
    5, // 核心线程数
    20, // 最大线程数
    5, // 空闲线程回收的时间
    TimeUnit.SECONDS, // 时间单位
    new LinkedBlockingQueue(20), // 队列
    Executors.defaultThreadFactory(), // 工厂
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
使用线程池的好处
  • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度:当任务到达时,可以不需要等待线程创建就能立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,监控和调优。

异步编程

模拟用户下单操作。。。

1、根据地址id查询地址信息 – > 0.5s

2、查询用户购物车的数据 – >0.5s

3、查询购物车中的商品信息 – > 1s

4、创建订单 – > 0.5s

6、创建订单详情 --> 0.5s

用户创建要给一个订单总共耗时3s,没给操作都是同步执行的。如果变成异步是否会提高性能?

CompletableFuture

Java8新增了CompletableFuture 提供对异步计算的支持,可以通过回调的方式处理计算结果。

runAsync 和 supplyAsync方法
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)

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

没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。

  • runAsync方法不支持返回值。
  • supplyAsync可以支持返回值。
使用默认和指定的线程池
       // 创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        System.out.println("线程开始。。。");

        // 使用默认的线程池
        CompletableFuture.runAsync(() -> {
            System.out.println("任务开始执行。。");
        });

        // 使用指定的线程池
        CompletableFuture.runAsync(() ->{
            System.out.println("任务2开始执行。。。");
        },executorService);

        System.out.println("线程结束");
线程执行完后有返回值
   		 // 创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        System.out.println("线程开始。。。");

		// 使用指定线程池运行一个任务
       CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Integer sum = 0;
            for (int i = 1; i <=5; i++) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sum += i;
            }
            return sum;
        }, executorService);

		// get()阻塞等结果
        System.out.println("sum:"+voidCompletableFuture.get());
        System.out.println("线程结束");
计算结果完成时的回调方法

当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)

可以看到Action的类型是BiConsumer<? super T,? super Throwable>它可以处理正常的计算结果,或者异常情况。

whenComplete 和 whenCompleteAsync 的区别:
whenComplete:是执行当前任务的线程继续执行 whenComplete 的任务。
whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。

exceptionally:任务运行出现异常后调用,在这个方法里面可以改变任务的返回值(降级)。

        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务线程:"+Thread.currentThread().getName());
            Integer sum = 0;
            for (int i = 1; i <= 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int x = 10 / 0;
                sum += i;
            }
            return sum;
        }, executorService).whenComplete((resp, exc) -> {
//            System.out.println("任务执行完了,返回结果是:" + resp + ",异常:" + exc);
            System.out.println("whenComplete:"+Thread.currentThread().getName());
        }).whenCompleteAsync((resp,exc)->{
            System.out.println("whenCompleteAsync:"+Thread.currentThread().getName());
        },executorService).exceptionally((exc) -> {
//            System.out.println("出现异常了,这个方法里面可以修改返回结果");
            return 20;
        });
        System.out.println("sum:" + integerCompletableFuture.get());
handle

handle 是执行任务完成时对结果的处理。

exceptionally和handle同时存在,handle 的返回值会覆盖掉exceptionally的。whenCompleteAsync是感知任务执行完了,而handle是任务执行完了真真的调用

   CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务线程:"+Thread.currentThread().getName());
            Integer sum = 0;
            for (int i = 1; i <= 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int x = 10 / 0;
                sum += i;
            }
            return sum;
        }, executorService).handle((resp,exc)->{
            System.out.println("handle:resp:"+resp+",exc:"+exc);
            return 22;
        });
        System.out.println("sum:" + integerCompletableFuture.get());

任务串行化

前一个任务执行完才能执行后一个任务。

// 不依赖前面任务的执行结果,只要前面任务执行完了,该任务就马上执行
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)

// 接收前一个任务的执行结果,并消费处理,该任务没有返回结果    
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)

 //接收前一个任务的执行结果,并消费,该任务有返回结果  
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)
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1开始执行,计算结果返回");
            int x = 10 * 2;
            return x;
        }, executorService);
        integerCompletableFuture.thenRunAsync(() -> {
            System.out.println("任务2开始执行,这里不需要依赖任务1的返回值");
        }, executorService);
        integerCompletableFuture.thenAcceptAsync((resp)->{
            System.out.println("任务3开始执行,这里需要依赖到任务1中的返回值:"+resp+",该任务没有返回值");
        },executorService);

        CompletableFuture<String> stringCompletableFuture = integerCompletableFuture.thenApplyAsync((resp) -> {
            System.out.println("任务4开始执行,这里需要依赖到任务1中的返回值:" + resp + ",该任务有回值");
            return "任务4:" + resp;
        }, executorService);

        System.out.println("sum:" + stringCompletableFuture.get());

两个任务组合-- 都要完成

两个任务都要完成以后,再去触发第三个任务。

// 组合两个任务,不需要获取前两个任务的结果,之前前两个任务执行完后就执行该任务
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action,Executor executor)

// 组合两个任务,需要前两个任务的返回结果,执行该任务后没有返回结果  
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)

// 组合两个任务,需要前两个任务的返回结果,执行该任务后有返回结果  
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) 
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)
  
        // 第1个任务
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1执行");
            return 10;
        }, executorService);

        // 第2个任务
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2执行");
            return "java";
        }, executorService);

        // 组合前两个任务
//        future1.runAfterBothAsync(future2, () -> {
//            System.out.println("前两个任务执行后在执行该任务,不需要前两个任务的结果,该任务不需要返回");
//        }, executorService);

//        future1.thenAcceptBothAsync(future2, (f1, f2) -> {
//            System.out.println("前两个任务执行后在执行该个任务,需要前两个任务的结果:" + f1 + " -- " + f2 + ",该任务不需要返回");
//        }, executorService);

        CompletableFuture<String> stringCompletableFuture = future1.thenCombineAsync(future2, (f1, f2) -> {
            System.out.println("前两个任务执行后在执行该个任务,需要前两个任务的结果:" + f1 + " -- " + f2 + ",该任务需要返回");
            return f1 + f2;
        }, executorService);

        System.out.println("sum:" + stringCompletableFuture.get());

多任务组合

allof顾名思义,就是所有的任务执行完成后返回future,
anyif就是只要有一个任务执行完成后就返回future并将第一个完成的参数带着一起返回,

       CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(5000);
            System.out.println("任务1查询用户地址");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "addressList";
        }, executorService);

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2查询用户购物车");
            return "carList";
        }, executorService);

        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务3查询购物车商品");
            return "goodsList";
        }, executorService);

        // 组合多个任务,所有任务都要完成
//        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(future1, future2, future3);
//        voidCompletableFuture.get(); // 等待所有任务完成。

        // 组合多个任务,只要有一个任务完成
        CompletableFuture<Object> objectCompletableFuture = CompletableFuture.anyOf(future1, future2, future3);
        objectCompletableFuture.get(); // 等待所有任务完成。

//        System.out.println("显示地址:" + future1.get());
//        System.out.println("显示购物车:" + future2.get());
//        System.out.println("显示商品:" + future3.get());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值