并发举措六之线程池

        前言

        请大家优先阅读美团技术团队写的Java线程池实现原理及其在美团业务中的实践。https://blog.csdn.net/heijunwei/article/details/112985523,本文仅仅是对这篇文章没有涉及的知识点而且比较重要的补充,狗尾续貂。

        executort优先于线程,jdk1.5中已经增加了java.util.concurrent。这个字包中包含了一个ExecutorFramework它是一个很灵活的基于接口的任务执行工具。

       线程池性能优于线程:Java语言虽然内置了多线程支持,启动一个新线程非常方便,但是,创建线程需要操作系统资源(线程资源,栈空间等),频繁创建和销毁大量线程需要消耗大量时间,那么我们就可以把很多小任务让一组线程来执行,而不是一个任务对应一个新线程。简单地说,线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待状态。如果有新任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,新任务要么放入队列等待,要么增加一个新线程进行处理。

      线程池提供了更丰富易用的功能:,可以等待完成一项特殊的任务,可以等待一个任务集合中的任何任务或者所有任务完成(利用invokeAny或者invokeAll方法),可以等待executorservice优雅地完成终止(利用awaitTermination方法),可以在任务完成时逐个地获取这些任务的结果(利用ExecutorCompletionService),可以调度在某个特殊的时间段定时运行或者阶段性地运行的任务(利用ScheduledThreadPoolExecutor),等等。

      一、线程池分类:

     Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。


     1.1、newCachedThreadPool

     创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。


     1.2、newFixedThreadPool

       创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。


     1.3、newScheduledThreadPool

     创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。(定时器)


     1.4、newSingleThreadExecutor

      Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!

    

   二、线程池应用实例:

    2.1、newFixedThreadPool应用

public class Main {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池:
        ExecutorService es = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 6; i++) {
            es.submit(new Task("" + i));
        }
        // 关闭线程池:
        es.shutdown();
    }
}

class Task implements Runnable {
    private final String name;

    public Task(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("start task " + name);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.println("end task " + name);
    }
}

2.2、newScheduledThreadPool应用

ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);
scheduledThreadPool.schedule(new Runnable(){
    @Override
    public void run() {
        System.out.println("延迟三秒");
    }
 }, 3, TimeUnit.SECONDS);

scheduledThreadPool.scheduleAtFixedRate(new Runnable(){
    @Override
    public void run() {
        System.out.println("延迟1秒后每三秒执行一次");
    }
 },1,3,TimeUnit.SECONDS);

2.3、Future应用

class Task implements Callable<String> {
    public String call() throws Exception {
        return longTimeCalculation(); 
    }
}
ExecutorService executor = Executors.newFixedThreadPool(4); 
// 定义任务:
Callable<String> task = new Task();
// 提交任务并获得Future:
Future<String> future = executor.submit(task);
// 从Future获取异步执行返回的结果:
String result = future.get(); // 可能阻塞

当我们提交一个Callable任务后,我们会同时获得一个Future对象,然后,我们在主线程某个时刻调用Future对象的get()方法,就可以获得异步执行的结果。在调用get()时,如果异步任务已经完成,我们就直接获得结果。如果异步任务还没有完成,那么get()会阻塞,直到任务完成后才返回结果。

一个Future<V>接口表示一个未来可能会返回的结果,它定义的方法有:

  • get():获取结果(可能会等待)
  • get(long timeout, TimeUnit unit):获取结果,但只等待指定的时间;
  • cancel(boolean mayInterruptIfRunning):取消当前任务;
  • isDone():判断任务是否已完成。

2.4、CompletableFuture应用

public class Main {
    public static void main(String[] args) throws Exception {
        // 两个CompletableFuture执行异步查询:
        CompletableFuture<String> completableFutureLottery = CompletableFuture.supplyAsync(() -> {
            return queryLucyUser("多样抽奖", "0");
        });
        CompletableFuture<String> completableFutureBox = CompletableFuture.supplyAsync(() -> {
            return queryLucyUser("宝箱抽奖", "1");
        });

        // 用anyOf合并为一个新的CompletableFuture:
        CompletableFuture<Object> completableFuture = CompletableFuture.anyOf(completableFutureLottery, completableFutureBox);

        // 两个CompletableFuture执行异步查询:
        CompletableFuture<Double> cfFetchFromLottery = completableFuture.thenApplyAsync((code) -> {
            return fetchLotteryPrice((String) code, "hjw_lottery");
        });
        CompletableFuture<Double> cfFetchFromBox = completableFuture.thenApplyAsync((code) -> {
            return fetchLotteryPrice((String) code, "hjw_box");
        });

        // 用anyOf合并为一个新的CompletableFuture:
        CompletableFuture<Object> future = CompletableFuture.anyOf(cfFetchFromLottery, cfFetchFromBox);

        // 最终结果:
        future.thenAccept((result) -> {
            System.out.println("奖项价格: " + result);
        });
        // 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
        Thread.sleep(200);
    }

    static String queryLucyUser(String name, String type) {
        System.out.println("queryLucyUser " + name + "...");
        try {
            Thread.sleep((long) (Math.random() * 200));
        } catch (InterruptedException e) {
        }
        return "hjw";
    }

    static Double fetchLotteryPrice(String code, String name) {
        System.out.println("fetchLucyUser " + name + "...");
        try {
            Thread.sleep((long) (Math.random() * 200));
        } catch (InterruptedException e) {
        }
        return 5 + Math.random() * 20;
    }
}

使用Future获得异步执行结果时,要么调用阻塞方法get(),要么轮询看isDone()是否为true,这两种方法都不是很好,因为主线程也会被迫等待。从Java 8开始引入了CompletableFuture,它针对Future做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值