手写单例模式的线程池实践

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_20009015/article/details/89981008

场景,有两个接口分别会接收两个数据,如果其中一个数据校验失败,那么另一个数据也要删掉。

由于是两个独立的数据源,没法做事务的回滚。( 这两个数据源是来自canal的监听得到的)。

因此考虑的方案就是,当校验不通过的时候,通过异步的方式,去起一个线程去删掉另一个接口过来的任务,但是这两个数据过来会有延时几秒,因此在另一个线程里面,先休眠3秒在执行删除。

因此考虑用一个线程池,防止校验不通过的数据过多导致创建过多的线程让系统出现问题。

线程池的参数:
1.第一个参数是核心线程数 2个
2.第二个参数是最大线程数10个
3.第三个参数是空闲线程存活时间60秒
4.第四个参数是时间单位 秒
5.线程任务队列,设置成了100个任务, 有界队列,如果队列满了,就会创建新的线程 ,最大不超过10个。 如果还有任务,就会执行拒绝策略
6.线程工厂,继承了ThreadFactor ,用于创建线程,设置了线程名称 ,方便后续出问题排查
7.拒绝策略,这里的策略是直接放弃任务,并打印日志。

 new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100),
                    (Runnable r) -> {
                        Thread t = new Thread(r);
                        t.setName("删除校验失败的对帐单关联的保险数据线程");
                        log.info("启动"+t.getName());
                        return new Thread(r);
                    }, (Runnable r, ThreadPoolExecutor executor) -> {
                log.error("已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了");
            });

拒绝策略,默认的几种拒绝策略有 1.直接抛异常,2.忽视掉,3.丢掉最老的任务,4.让当初线程执行

这里我们选择忽视掉并打印日志。

得用单例模式,保证线程池的唯一,不至于每一次都创建一个新的线程池。不然这个线程池就没意义了。

懒汉模式,通过静态内部类实现,因类只要用的时候才会被加载,且只加载一次,且加载过程是加锁的(jvm实现)

public class CheckAccountThreadPoolUtil {

//静态方法获取对象
    public static ExecutorService getWorkPoolUtil() {
    //调用内部静态类的静态方法获取对象 
    //这个时候触发内部类的加载 也就是只有在用的时候才会加载,懒汉模式
        return Helper.getInstance();
    }

//内部类加载之后 
    private static class Helper {
     //静态字段  在类加载的时候被初始化  且只初始化一次
        private static ExecutorService instance=new ExecutorService(){}.... ;
        //获取对象
        public static ExecutorService getInstance() {
            return instance;
        }
    }

单例和非单例的验证

非单例

public class CheckAccountThreadPoolUtil {


    public static ExecutorService getWorkPoolUtil() {

        return Helper.getInstance();
    }

    private static class Helper {

        private static ExecutorService instance;

        public static ExecutorService getInstance() {
            if (instance!=null){
                return instance;
            }
            instance = new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100),
                    (Runnable r) -> {
                        Thread t = new Thread(r);
                        t.setName("删除校验失败的对帐单关联的保险数据线程");
                        log.info("启动"+t.getName());
                        return new Thread(r);
                    }, (Runnable r, ThreadPoolExecutor executor) -> {
                log.error("已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了");
            });
            return instance;
        }
    }
    }

十个线程同时获取如下

public static void main(String[] args) throws InterruptedException, IOException {

        //同时启动10个 看是否是单例
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                log.info("进入等待");
                countDownLatch.countDown();
                try {
                    //在这个节点等待countDownLatch减少到0
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("等待结束");

                System.out.println("executorService:" + CheckAccountThreadPoolUtil.getWorkPoolUtil().hashCode());
            }).start();

            System.out.println("启动线程数"+(i+1));
        }
        }

结果

启动线程数1
启动线程数2
启动线程数3
启动线程数4
启动线程数5
启动线程数6
启动线程数7
启动线程数8
启动线程数9
启动线程数10
00:05:19.932 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.932 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.932 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.932 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.932 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.933 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:05:19.936 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:05:19.936 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:1188407569
executorService:1730989841
executorService:1748713658
executorService:693173612
executorService:2005060830
executorService:1390979302
executorService:2082070238
executorService:1303637660
executorService:2098748826
executorService:1338817235

可以看到,每个hashcode都不一样 。
改成单例之后如下

public class CheckAccountThreadPoolUtil {


    public static ExecutorService getWorkPoolUtil() {

        return Helper.getInstance();
    }

    private static class Helper {

        private static ExecutorService instance =
                new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100),
                (Runnable r) -> {
                    Thread t = new Thread(r);
                    t.setName("删除校验失败的对帐单关联的保险数据线程");
                    log.info("启动"+t.getName());
                    return new Thread(r);
                }, (Runnable r, ThreadPoolExecutor executor) -> {
            log.error("已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了");
        });

        
        public static ExecutorService getInstance() {
          
            return instance;
        }
    }
}

测试验证代码不变 ,结果如下:

启动线程数1
启动线程数2
启动线程数3
启动线程数4
启动线程数5
启动线程数6
启动线程数7
启动线程数8
启动线程数9
启动线程数10
00:11:20.293 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.293 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
00:11:20.298 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
00:11:20.298 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850
executorService:1840045850


package com.souche.sfs.server.common.util;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.concurrent.*;

/**
 * @ClassName CheckAccountThreadPoolUtil
 * @Author laixiaoxing
 * @Date 2019/5/8 下午4:45
 * @Description 用来异步删掉校验失败的对账单对应的保险数据的线程池
 * @Version 1.0
 */
@Slf4j
public class CheckAccountThreadPoolUtil {


    public static ExecutorService getWorkPoolUtil() {

        return Helper.getInstance();
    }

    private static class Helper {

        private static ExecutorService instance=
           new ThreadPoolExecutor(2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(100),
                    (Runnable r) -> {
                        Thread t = new Thread(r);
                        t.setName("删除校验失败的对帐单关联的保险数据线程");
                        log.info("启动"+t.getName());
                        return new Thread(r);
                    }, (Runnable r, ThreadPoolExecutor executor) -> {
                log.error("已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了");
            });

        public static ExecutorService getInstance() {
            return instance;
        }
    }


    //测试
    public static void main(String[] args) throws InterruptedException, IOException {
        //挨个获取多个 看是否是单例
        System.out.println("顺序启动获取");
        ExecutorService executorService = CheckAccountThreadPoolUtil.getWorkPoolUtil();
        System.out.println(executorService.hashCode());
        ExecutorService executorService2 = CheckAccountThreadPoolUtil.getWorkPoolUtil();
        System.out.println(executorService2.hashCode());

        //同时启动10个 看是否是单例
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                log.info("进入等待");
                //计数器减1
                countDownLatch.countDown();
                try {
                    //在这个节点等待countDownLatch减少到0
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("等待结束");

                System.out.println("executorService:" + CheckAccountThreadPoolUtil.getWorkPoolUtil().hashCode());
            }).start();

            System.out.println("启动线程数"+(i+1));
        }



        //启动一批线程放进去
        for (int i = 0; i < 200; i++) {
            executorService.submit(()->{
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });

        }

        System.in.read();
    }


}


测试拒绝策略如下

顺序启动获取
314337396
314337396
启动线程数1
启动线程数2
启动线程数3
启动线程数4
启动线程数5
启动线程数6
启动线程数7
启动线程数8
启动线程数9
启动线程数10
23:25:40.656 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.655 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.659 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.655 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.655 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.656 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 进入等待
23:25:40.661 [Thread-9] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
23:25:40.661 [Thread-7] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
executorService:314337396
23:25:40.661 [Thread-5] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-4] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-2] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-3] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-8] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-1] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
23:25:40.661 [Thread-0] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
23:25:40.661 [Thread-6] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 等待结束
executorService:314337396
executorService:314337396
23:25:40.660 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.663 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.664 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.664 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.664 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] INFO com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 启动删除校验失败的对帐单关联的保险数据线程
23:25:40.665 [main] ERROR com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了
23:25:40.665 [main] ERROR com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了
23:25:40.665 [main] ERROR com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了
23:25:40.665 [main] ERROR com.souche.sfs.server.common.util.CheckAccountThreadPoolUtil - 已超出最大任务数,那么校验不通过的对账单数据,狗日的系统出问题了,删不过来了

猜你喜欢

转载自blog.csdn.net/qq_20009015/article/details/89981008