• Java并发程序设计(五)JDK并发包之线程复用:线程池


    线程复用:线程池

    一、为什么需要线程池

    为了避免系统频繁地创建和销毁线程,使用线程池让线程进行复用。(即创建线程变成了从线程池中获取空闲线程,销毁线程变成了把线程放回线程池中。)

    二、JDK对线程池的支持:Executor框架

    一)Excutor框架简介

    public static ExecutorService newFixedThreadPool(int Threads) //返回一个固定线程数量的线程池,当新任务提交时无空闲线程,则任务进入等待队列,等待空闲线程。
    
    public static ExecutorService newSingleThreadExecutor() //返回一个只有一个线程的线程池,多余的任务被提交到线程池,会进入等待队列,按先入先出顺序执行。
    
    public static ExecutorService newCahedThreadPool() //返回一个可根据实际情况调整线程数量的线程池
    
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() //线程池中的线程可根据时间执行任务。
    
    public static ScheduledExecutorService newScheduledThreadPool()
    public class SESDemo {
    
        public static void main(String[] args){
            ScheduledExecutorService p = Executors.newScheduledThreadPool(10);
            p.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try{
                        Thread.sleep(1000);
                        System.out.println(System.currentTimeMillis()/1000+" and name is "+
                                Thread.currentThread().getName());
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            },0,2, TimeUnit.SECONDS);
        }
    }
    /*1504236959 and name is pool-1-thread-1
    1504236961 and name is pool-1-thread-1
    1504236963 and name is pool-1-thread-2
    1504236965 and name is pool-1-thread-1
    1504236967 and name is pool-1-thread-3
    1504236969 and name is pool-1-thread-2
    .......
    1504237017 and name is pool-1-thread-7
    1504237019 and name is pool-1-thread-7
    1504237021 and name is pool-1-thread-7
    1504237023 and name is pool-1-thread-7
    1504237025 and name is pool-1-thread-7
    */

    注意:任务本身不能抛出异常,不然后续所有执行都会被中断。

    二)框架内部实现:

    以上方法具体实现都使用了ThreadPoolExecutor,它们都封装了ThreadPoolExecutor

    其中workQueue指被提交但未被执行的任务队列。

     拒绝策略:线程池中的线程已经用完,队列也已经排满。

    JDK内置的四种拒绝策略:

    AbortPolicy:直接抛出异常,阻止系统正常执行。

    CallerRunsPolicy:直接在任务提交的线程内执行线程。

    DiscardOledestPolicy:丢弃最老的一个任务,并再次提交当前任务。

    DiscardPolicy:丢弃无法处理的当前任务,不做其他处理。

    以上策略都实现了RejectedExecutionHandler接口,如果以上策略无法满足我们的需求,我们可以扩展该接口。

    public class RejectThreadPoolDemo {
        public static class MyTask implements Runnable{
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()+" :Thread ID:"+
                Thread.currentThread().getId());
    
                try{
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            MyTask myTask=new MyTask();
            ThreadPoolExecutor es = new ThreadPoolExecutor(5, 5, 0L,
                    TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(10)
                    , Executors.defaultThreadFactory(),
                    new RejectedExecutionHandler() {
                        @Override
                        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                            System.out.println(r.toString() + "is discard");
                        }
                    });
    
            for (int i=0;i<Integer.MAX_VALUE;i++){
                es.submit(myTask);
                Thread.sleep(10);
            }
        }
    }

    三)自定义线程创建

    线程池中线程的来源:ThreadFactory接口。override该接口中的

    public Thread newThread(Runnable r);

    方法即可。

    四)扩展线程池

    ThreadPoolExecutor是个可以扩展的线程池接口,它提供了beforeExecutor(),afterExecutor()和terminated三个接口对线程池进行控制。

    public class ExtThreadPool {
    
        public static class Mytask implements Runnable{
            private String name;
    
            public Mytask(String name) {
                this.name = name;
            }
    
            @Override
            public void run() {
                System.out.println("正在执行:线程ID"+Thread.currentThread().getId()+
                "任务名:"+name);
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            ExecutorService es=new ThreadPoolExecutor(5,5,0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>()){
    
                @Override
                protected void beforeExecute(Thread t, Runnable r) {
                    System.out.println("准备执行:"+((Mytask)r).name);
                }
    
                @Override
                protected void afterExecute(Runnable r, Throwable t) {
                    System.out.println("执行完毕:"+((Mytask)r).name);
                }
    
                @Override
                protected void terminated() {
                    System.out.println("线程池退出");
                }
            };
    
            for (int i=0;i<5;i++){
                Mytask mytask=new Mytask("task"+i);
                es.execute(mytask);
                Thread.sleep(10);
            }
    
            es.shutdown(); //所有任务执行完毕后,关闭线程池。
        }
    }

    五)线程池中寻找异常堆栈

    解决多线程中幽灵般的异常:

    方法一:放弃submit()改用execute()

    方法二:extends ThreadPoolExecutor

    六)分而治之,Fork/Join框架

    分阶段对大量数据进行处理,然后对结果进行整合。

     互助精神:

    重要接口:

    public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)

    ForkJoinTask两个重要子类:

    Simple is important!
  • 相关阅读:
    AWR介绍使用
    hint使用
    部分索引类型介绍\索引重建否\索引压缩
    生成Oracle Statpack步骤
    自治事务
    append与nologging的使用
    聚簇表
    C语言杂谈——静态函数
    Qt之对话框设计——可扩展对话框
    C语言杂谈——指针篇
  • 原文地址:https://www.cnblogs.com/Shadowplay/p/7462763.html
Copyright © 2020-2023  润新知