• java.util.concurrent 学习笔记(2) 线程池基础



    在什么情况下使用线程池?


    (1).单个任务处理的时间比较短

    (2).将需处理的任务的数量大


    使用线程池的好处:


    (1)、减少在创建和销毁线程上所花的时间以及系统资源的开销

    (2)、如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。


    1、FixedThreadPool

    每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
    在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源

    public class FixedThreadPoolTest {
    
        public static void main(String[] args) {
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
            for (int i = 0; i < 10; i++) {
                Runnable task = new MyTask();
                System.out.println( " Main add Task : " + i);
                fixedThreadPool.execute(task);
            }
            fixedThreadPool.shutdown();
            System.out.println( "Main end ");
        }
    }
    
    class MyTask implements Runnable {
    
        @Override
        public void run() {
            Random random = new Random();
            System.out.println(Thread.currentThread().getName() + " begin ");
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    TimeUnit.SECONDS.sleep(random.nextInt(5));
                } catch (InterruptedException ex) {
                }
            }
            System.out.println(Thread.currentThread().getName() + " end ");
        }
    }


    
    
    
    

    输出:

       Main add Task : 0 

    Main add Task : 1 

    Main add Task : 2 

    Main add Task : 3

    pool-1-thread-1 begin  

    Main add Task : 4

    pool-1-thread-2 begin 

    pool-1-thread-2 : 0

    pool-1-thread-1 : 0

    pool-1-thread-2 : 1

    Main end 

    pool-1-thread-1 : 1

    pool-1-thread-1 : 2

    pool-1-thread-2 : 2

    pool-1-thread-1 : 3

    pool-1-thread-2 : 3

    pool-1-thread-1 : 4

    pool-1-thread-1 end 

    pool-1-thread-1 begin 

    pool-1-thread-1 : 0

    pool-1-thread-2 : 4

    pool-1-thread-2 end 

    pool-1-thread-2 begin 

    pool-1-thread-2 : 0

    pool-1-thread-1 : 1

    pool-1-thread-2 : 1

    pool-1-thread-2 : 2

    pool-1-thread-1 : 2

    pool-1-thread-2 : 3

    pool-1-thread-2 : 4

    pool-1-thread-1 : 3

    pool-1-thread-1 : 4

    pool-1-thread-1 end 

    pool-1-thread-2 end 

    pool-1-thread-2 begin 

    pool-1-thread-2 : 0

    pool-1-thread-2 : 1

    pool-1-thread-2 : 2

    pool-1-thread-2 : 3

    pool-1-thread-2 : 4

    pool-1-thread-2 end    

    结论:

    (1)、线程池大小为2,加入实际线程数大于2时候,线程池也不会阻塞的,也会加进去,只是不马上执行而已。

    (2)、线程池的线程可以复用,一直使用两个线程,而不是等线程结束以后,开始新的线程。

    (3)、excute() 不会阻塞主线程,即excute() 是异步的。

    (4)、线程池必须使用shutdown来显式关闭,否则主线程就无法退出。shutdown也不会阻塞主线程,即shutdown是异步的

    2、SingleThreadExecutor

    单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。

    单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 5; i++) {
                Runnable task =  new MyTask();
                System.out.println( " Main add Task : " + i);
                singleThreadExecutor.execute(task);
            }
            singleThreadExecutor.shutdown();
            System.out.println( "Main end ");


    输出:

     Main add Task : 0
     Main add Task : 1
     Main add Task : 2
     Main add Task : 3
     Main add Task : 4
    pool-1-thread-1 begin 
    pool-1-thread-1 : 0
    Main end 
    pool-1-thread-1 : 1
    pool-1-thread-1 : 2
    pool-1-thread-1 : 3
    pool-1-thread-1 : 4
    pool-1-thread-1 end 
    pool-1-thread-1 begin 
    pool-1-thread-1 : 0
    pool-1-thread-1 : 1
    pool-1-thread-1 : 2
    pool-1-thread-1 : 3
    pool-1-thread-1 : 4
    pool-1-thread-1 end 
    pool-1-thread-1 begin 
    pool-1-thread-1 : 0
    pool-1-thread-1 : 1
    pool-1-thread-1 : 2
    pool-1-thread-1 : 3
    pool-1-thread-1 : 4
    pool-1-thread-1 end 
    pool-1-thread-1 begin 
    pool-1-thread-1 : 0
    pool-1-thread-1 : 1
    pool-1-thread-1 : 2
    pool-1-thread-1 : 3
    pool-1-thread-1 : 4
    pool-1-thread-1 end 
    pool-1-thread-1 begin 
    pool-1-thread-1 : 0
    pool-1-thread-1 : 1
    pool-1-thread-1 : 2
    pool-1-thread-1 : 3
    pool-1-thread-1 : 4
    pool-1-thread-1 end 


    结论:

    SingleThreadExecutor 也就是 newFixedThreadPool(1) 而已。


    3、CachedThreadPool

    (1). 工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger.MAX_VALUE), 这样可灵活的往线程池中添加线程。

    (2).  如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

    在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪


            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
            for (int i = 0; i < 5; i++) {
                Runnable task =  new MyTask();
                System.out.println( " Main add Task : " + i);
                cachedThreadPool.execute(task);
            }
            cachedThreadPool.shutdown();
            System.out.println( "Main end ");


    输出:

     Main add Task : 0
     Main add Task : 1
     Main add Task : 2
     Main add Task : 3
    pool-1-thread-1 begin 
    pool-1-thread-1 : 0
    pool-1-thread-2 begin 
     Main add Task : 4
    pool-1-thread-3 begin 
    pool-1-thread-3 : 0
    pool-1-thread-4 begin 
    pool-1-thread-4 : 0
    pool-1-thread-2 : 0
    pool-1-thread-2 : 1
    pool-1-thread-5 begin 
    pool-1-thread-5 : 0
    Main end 
    pool-1-thread-1 : 1
    pool-1-thread-2 : 2
    pool-1-thread-3 : 1
    pool-1-thread-5 : 1
    pool-1-thread-4 : 1
    pool-1-thread-1 : 2
    pool-1-thread-4 : 2
    pool-1-thread-1 : 3
    pool-1-thread-3 : 2
    pool-1-thread-2 : 3
    pool-1-thread-5 : 2
    pool-1-thread-2 : 4
    pool-1-thread-3 : 3
    pool-1-thread-3 : 4
    pool-1-thread-2 end 
    pool-1-thread-5 : 3
    pool-1-thread-4 : 3
    pool-1-thread-4 : 4
    pool-1-thread-3 end 
    pool-1-thread-1 : 4
    pool-1-thread-4 end 
    pool-1-thread-5 : 4
    pool-1-thread-1 end 
    pool-1-thread-5 end 


    结论:

    (1)、加入线程后马上执行,不会阻塞的,加多少就运行多少。 

    (3)、excute() 不会阻塞主线程,即excute() 是异步的。

    (4)、线程池必须使用shutdown来显式关闭,否则主线程就无法退出。shutdown也不会阻塞主线程,即shutdown是异步的。


    4、ScheduledThreadPool、SingleScheduledThreadPool

    创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer


            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
            for (int i = 0; i < 5; i++) {
                Runnable task =  new MyTask();
                System.out.println( " Main add Task : " + i);
                scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS);
            }
            scheduledThreadPool.shutdown();
           //  scheduledThreadPool.scheduleAtFixedRate(task, 1, 1, TimeUnit.HOURS);

                //scheduledThreadPool.scheduleWithFixedDelay(task, 1, 1, TimeUnit.DAYS); System.out.println( "Main end ");

    
    

    较好理解。

    5、Callable 和 Future

    class CallableTask implements Callable<Integer> {
    
        public CallableTask() {
        }
    
        @Override
        public Integer call() throws Exception {
            TimeUnit.SECONDS.sleep(new Random().nextInt(2));
            return new Random().nextInt();
        }
    }
    
    public class CallableTest {
    
        public static void main(String[] args) {
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
            List<Future> futures = new ArrayList<Future>();
            for (int i = 0; i < 10; i++) {
                Callable<Integer> task = new CallableTask();
                Future<Integer> future = fixedThreadPool.submit(task);
                futures.add(future);
            }
            for (Future<Integer> future : futures) {
                try {
                    if (future.isDone()) {
                        System.out.println("Done: " + future.get());
                    } else {
                        future.cancel(true);
                        System.out.println("Canceled: " + future.isCancelled());
                    }
                } catch (InterruptedException | ExecutionException ex) {
                    ex.printStackTrace();
                }
            }
            fixedThreadPool.shutdown();
            System.out.println("Main end ");
        }
    }


    
    
    

    输出:

    Done: 1282190456

    Done: -729572913Done: 2080564117Canceled: trueDone: 1361725593Canceled: trueDone: 2038751796Canceled: trueCanceled: trueDone: -1923347889Main end 

    结论:

    1、submit方法提交 Callable的任务,通过Future 获得结果

    2、Future 的get() 方法不马上返回结果,而是要在完成之后返回,

    3、Future 的 cancel()方法 取消任务

    4、Future 的 isCancel()方法  isDone() 方法返回状态。

    6、CompletionService

     public class CompletionServiceTest {
    
        public static void main(String[] args) {
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
            CompletionService completionService = new ExecutorCompletionService(fixedThreadPool);
            for (int i = 0; i < 10; i++) {
                Callable<Integer> task = new CallableTask2();
                completionService.submit(task);
            }
            for (int i = 0; i < 10; i++) {
                try {
                    Future take = completionService.take();
                    System.out.println(" end: " + take.get());
                } catch (InterruptedException | ExecutionException ex) {
                    ex.printStackTrace();
                }
            }
            fixedThreadPool.shutdown();
            System.out.println("Main end ");
        }
    }
    
    
    
    

    输出:

     end: -1156022272 

    end: -466547416 

    end: 1348425806 

    end: 558625149 

    end: 1573057831 

    end: 156442554 

    end: 309780892 

    end: -333067071 

    end: 906757921 

    end: -2083965743 

    Main end 

    结论:

    CompletionService接口有两个重要方法:submit()和take()。submit用于提交一个runnable或者callable,一般会提交给一个线程池处理;而take就是取出已经执行完毕runnable或者callable实例的Future对象,如果没有满足要求的,就等待了; 方法poll()与take类似,只是不会等待,如果没有满足要求,就返回null对象。

    public class InvokeAnyTest {
    
        public static void main(String[] args) {
    
            ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
            List<CallableTask> tasks = new LinkedList<CallableTask>();
            for (int i = 0; i < 10; i++) {
                CallableTask callableTask = new CallableTask(i + "");
                tasks.add(callableTask);
            }
            try {
                Integer result = newFixedThreadPool.invokeAny(tasks);
                System.out.println("first result at " + System.currentTimeMillis() + ":" + result);
            } catch (InterruptedException | ExecutionException ex) {
                System.out.println("invokeAny exception at " + System.currentTimeMillis() + ":" + ex.getMessage());
            }
    
            newFixedThreadPool.shutdown();
        }
    }
    
    class CallableTask implements Callable<Integer> {
    
        String name;
    
        public CallableTask(String name) {
            this.name = name;
        }
    
        @Override
        public Integer call() {
            try {
                TimeUnit.SECONDS.sleep(new Random().nextInt(5));
            } catch (InterruptedException ex) {
                System.out.println(this.name + " exception at " + System.currentTimeMillis() + ":" + ex.getMessage());
            }
            int nextInt = new Random().nextInt(1000);
            System.out.println(this.name + " return at " + System.currentTimeMillis() + ":" + nextInt);
            return nextInt;
        }
    


    
    
    

    输出:

    2 return at 1411354042128:781

    return at 1411354042128:1833 

    return at 1411354042128:2307 

    return at 1411354042129:795

    first result at 1411354042129:781

    6 exception at 1411354042130:sleep interrupted

    0 exception at 1411354042130:sleep interrupted

    5 exception at 1411354042130:sleep interrupted

    0 return at 1411354042130:435

    8 exception at 1411354042130:sleep interrupted

    8 return at 1411354042130:939

    1 exception at 1411354042130:sleep interrupted

    6 return at 1411354042130:428

    1 return at 1411354042130:519

    5 return at 1411354042130:807

    结论:

    1、只返回最早那一个结果

    2、其余没完成的线程中断,结果忽略。

    8. InvokeAll

    public class InvokeAllTest {
    
        public static void main(String[] args) {
    
            ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
            List<CallableTask2> tasks = new LinkedList<CallableTask2>();
            for (int i = 0; i < 10; i++) {
                CallableTask2 callableTask = new CallableTask2(i + "");
                tasks.add(callableTask);
            }
            try {
                List<Future<Integer>> listReslut = newFixedThreadPool.invokeAll(tasks);
                for (Future<Integer> result : listReslut) {
                    System.out.println(" result :" + result.get());
    
                }
            } catch (InterruptedException | ExecutionException ex) {
                System.out.println("invokeAny exception at " + System.currentTimeMillis() + ":" + ex.getMessage());
            }
            newFixedThreadPool.shutdown();
        }
    }
    
    class CallableTask2 implements Callable<Integer> {
    
        String name;
    
        public CallableTask2(String name) {
            this.name = name;
        }
    
        @Override
        public Integer call() {
            try {
                TimeUnit.SECONDS.sleep(new Random().nextInt(5));
            } catch (InterruptedException ex) {
                System.out.println(this.name + " exception at " + System.currentTimeMillis() + ":" + ex.getMessage());
            }
            int nextInt = new Random().nextInt(1000);
            System.out.println(this.name + " return at " + System.currentTimeMillis() + ":" + nextInt);
            return nextInt;
        }
    }


    输出:

    1 return at 1411354530402:993
    3 return at 1411354531402:706
    4 return at 1411354533402:998
    0 return at 1411354534402:116
    2 return at 1411354534402:432
    6 return at 1411354534402:298
    5 return at 1411354534402:206
    8 return at 1411354534402:812
    7 return at 1411354535402:57
    9 return at 1411354537402:957
     result :116
     result :993
     result :432
     result :706
     result :998
     result :206
     result :298
     result :57
     result :812
     result :957


    结论:全部任务完成再返回。


    7、FutureTask


    FutureTask 实现 Runnable 和Future 接口,没有实现Callable接口。


    public class FutureTask<V> implements RunnableFuture<V>
    public interface RunnableFuture<V> extends Runnable, Future<V> {
    
        void run();
    }
    public interface Future<V> {
     
        boolean cancel(boolean mayInterruptIfRunning);
     
        boolean isCancelled();
     
        boolean isDone();
     
        V get() throws InterruptedException, ExecutionException;
      
        V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    }

    例子

    public class FutureTaskTest {
    
        public static void main(String[] args) throws InterruptedException, ExecutionException {
    
            ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
            
            //run FutureTask by ThreadPool 
            List<FutureTask> futureTasks = new LinkedList<FutureTask>();
            for (int i = 0; i < 5; i++) {
                CallableTask callableTask = new CallableTask(i + "--pool");
                FutureTask ft = new FutureTask(callableTask);
                futureTasks.add(ft);
                newFixedThreadPool.execute(ft);
            }
            for (FutureTask ft : futureTasks) {
                System.out.println("By ThreadPool : " + ft.get());
            }
    
              //run FutureTask by standalone Thread
            for (int i = 0; i < 5; i++) {
                CallableTask callableTask = new CallableTask(i + "--alone");
                FutureTask ft = new FutureTask(callableTask);
                new Thread(ft).start();
            }
            for (FutureTask ft : futureTasks) {
                System.out.println("Standalone Thread : " + ft.get());
            }
    
            newFixedThreadPool.shutdown();
        }
    }
    




    输出:

    0--pool return at 1411356206910:86
    By ThreadPool : 86
    1--pool return at 1411356208910:128
    By ThreadPool : 128
    2--pool return at 1411356208911:109
    By ThreadPool : 109
    3--pool return at 1411356211910:258
    By ThreadPool : 258
    4--pool return at 1411356212911:792
    By ThreadPool : 792
    1--alone return at 1411356212912:556
    Standalone Thread : 86
    Standalone Thread : 128
    Standalone Thread : 109
    Standalone Thread : 258
    Standalone Thread : 792
    4--alone return at 1411356214913:404
    0--alone return at 1411356215913:949
    3--alone return at 1411356215914:507
    2--alone return at 1411356216913:769


    8、自定义线程池的分析和使用

    我们可以通过ThreadPoolExecutor来创建一个线程池。

    new  ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);


    创建一个线程池需要输入几个参数:


    corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。

    runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。

                ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。

                LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。

                SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。

                PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

    maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。

    threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。

    RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是                     AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。

                 AbortPolicy:直接抛出异常。

                 CallerRunsPolicy:只用调用者所在线程来运行任务。

                 DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。

                 DiscardPolicy:不处理,丢弃掉。

                 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

    keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。

    TimeUnit(线程活动保持时间的单位):可选的单位有DAYS,HOURS,MINUTES,MILLISECONDS,MICROSECONDS,NANOSECONDS



    当提交一个新任务到线程池时,线程池的处理流程如下:

    1.   首先线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务。满了,则进入下个流程。

    2.   其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。

    3.   最后线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务


  • 相关阅读:
    coolify heroku & netlify 可选开源方案
    signoz reader 接口定义
    minio 纠删码测试
    minio 系统自动纠删码处理算法简单说明
    apm + tracing 一些开源工具参考资料
    minio 4*4 集群 故障测试
    mimir grafana 部署模式
    temporal 开源微服务编排引擎
    nocodb 核心入口依赖
    hammerdb 数据库负载以及性能测试工具
  • 原文地址:https://www.cnblogs.com/leeeee/p/7276425.html
Copyright © 2020-2023  润新知