zoukankan      html  css  js  c++  java
  • Java线程池的使用

    频繁的创建和销毁线程需要花费大量时间,Java支持创建线程池,将多任务交给一组线程执行,提高执行效率。

    1、创建线程池

    Java标准库提供 ExecutorService 接口表示线程池,实现类有FixedThreadPool、CachedThreadPool、SingleThreadExecutor;创建线程池的方法被封装到Executors类。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ThreadPool {
        public static void main(String[] args) {
            ExecutorService es = Executors.newFixedThreadPool(6);
            for (int i = 0; i < 10; i++) {
                es.submit(new Task(i));
            }
            es.shutdown();
        }
    }
    
    class Task implements Runnable {
        private int count = 0;
        Task(int count) {
            this.count = count;
        }
        @Override
        public void run() {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.count);
        }
    }
    // 2
    // 4
    // 1
    // 5
    // 3
    // 0
    
    // 8
    // 6
    // 9
    // 7

    由于FixedThreadPool 总是创建固定的线程数量,当一次性提交的任务数量超过线程池中线程的数时,多出的任务需要等待。总结:

    • FixedThreadPool:创建指定数量线程的线程池;
    • CachedThreadPool:根据任务动态调整线程数的线程池,同时支持创建线程动态范围的线程池;
    • SingleThreadExecutor:单线程执行的线程池。

    线程池结束方式:

    • es.shutdown() :等待任务完成后,关闭线程池;
    • es.shutdownNow(): 立即结束任务,并关闭线程池;
    • es.awaitTermination(): 指定等待时间后,关闭线程池。

    2、ScheduledThreadPool

    ScheduledThreadPool 重复执行任务的线程池,Java标准库使用 ScheduledExecutorService 表示接口,创建方法同样被封装到Executors中。

    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class ThreadPool {
        public static void main(String[] args) throws InterruptedException {
            ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
    ses.schedule(
    new ScheduledTask(), 1, TimeUnit.SECONDS); ses.scheduleAtFixedRate(new ScheduledTask(), 0, 1, TimeUnit.SECONDS);      //ses.awaitTermination(10, TimeUnit.SECONDS); Thread.sleep(10000); ses.shutdownNow(); } } class ScheduledTask implements Runnable { private int i = 0; @Override public void run() { System.out.println(i++); } }

    其中:

    • ses.schedule(new ScheduledTask(), 1, TimeUnit.SECONDS): 表示1秒后执行任务;
    • ses.scheduleAtFixedRate(new ScheduledTask(), 0, 1, TimeUnit.SECONDS):表示0秒后,每 1 秒执行一次;
    • ses.scheduleWithFixedDelay(new ScheduledTask(), 0, 1, TimeUnit.SECONDS):表示0秒后,每间隔 1 秒执行一次;

    3、Future对象

    使用线程池可以方便的处理多任务,只需要任务实现Runable接口,调用submit方法提交执行即可。但缺点是无法返回值,Java提供Future对象返回执行结果,但异步任务需要实现Callable接口。

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    
    public class ThreadPool {
    
        public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
            ExecutorService executor = Executors.newFixedThreadPool(4); 
            Callable<String> task = new Task();
            Future<String> future = executor.submit(task);
            String res = future.get(1, TimeUnit.SECONDS);
            System.out.println(res);
        }
    }

    class Task implements Callable<String>{ public String call() throws InterruptedException { Thread.sleep(2000); //模拟耗时任务 return "done"; } }

    可以看到 Callable接口是泛型接口,可以指定返回类型。需要注意 future对象:

    • future.get()直接调用可能会阻塞当前线程;
    • future.get(long time, TimeUnit unit)可指定等待时间,超时会报timeoutException;
    • future.cancel()取消任务;
    • future.isDone(),判断任务是否完成。

     4、CompletableFuture

     使用Future获取异步执行的结果或阻塞当前线程,Java 8引入的CompletableFuture,支持异步回调,其用法类似JavaScript中的Promise对象的用法。

    import java.util.concurrent.CompletableFuture;
    import java.util.function.Supplier;
    
    public class ThreadPool {
        public static void main(String[] args) throws InterruptedException {
            // 异步执行
            CompletableFuture<String> apple = CompletableFuture.supplyAsync(new FetchApple());
            apple.thenAccept((result) -> {
                System.out.println(result);
            });
            apple.exceptionally((e) -> {
                e.printStackTrace();
                return null;
            });
            Thread.sleep(3000);
        }
    }
    
    class FetchApple implements Supplier<String> {
        @Override
        public String get() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Apple";
        }
    }

    CompletableFuture支持传入成功和失败的回调,在将来的时刻执行,并且不会阻塞当前线程。同时 CompletableFuture支持类似于Promise的链式回调,Promise.race的竞争并发执行和类似Promise.all的全部执行,但是用法上会复杂许多。CompletableFuture会使用默认线程池

    由于Java的严谨性,CompletableFuture.supplyAsync传入的对象需要实现Supplier<T>接口、成功的回调thenAccept传入对象需要实现Consumer<T>接口、失败的回调exceptionally需要传入实现Function<T,R>接口的对象。但好在支持的lambda语法可以进行简化。

    参考链接:

    https://www.liaoxuefeng.com/wiki/1252599548343744/1306581155184674 

  • 相关阅读:
    【ELK】重置 elasticsearch 的超管 elastic 账号密码
    python两种获取剪贴板内容的方法
    python创建列表和向列表添加元素方法
    7道Python函数相关的练习题
    Python中的那些“坑”
    Python基础教程:copy()和deepcopy()
    Python教程:optparse—命令行选项解析器
    Python教程:面向对象编程的一些知识点总结
    第一个pypi项目发布成功
    一个Markdown文件处理程序
  • 原文地址:https://www.cnblogs.com/engeng/p/15566116.html
Copyright © 2011-2022 走看看