package com.njzscloud.common.core.thread;


import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class ThreadPool {
    private static volatile ThreadPoolExecutor THREAD_POOL;

    public static ThreadPoolExecutor defaultThreadPool() {
        if (THREAD_POOL == null) {
            synchronized (ThreadPool.class) {
                if (THREAD_POOL == null) {
                    THREAD_POOL = createThreadPool(null,
                            10, 200,
                            5 * 60,
                            20, 2000,
                            null);
                }
            }
        }
        return THREAD_POOL;
    }

    public static ThreadPoolExecutor createThreadPool(String poolName, int corePoolSize, int maxPoolSize, long keepAliveSeconds, int windowCapacity, int standbyCapacity, RejectedExecutionHandler abortPolicy) {
        RejectedExecutionHandler abortPolicy_ = abortPolicy == null ? new ThreadPoolExecutor.AbortPolicy() : abortPolicy;
        Q<Runnable> q = new Q<>(windowCapacity, standbyCapacity);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS,
                q,
                new DefaultThreadFactory(poolName),
                (r, p) -> {
                    if (!q.offerStandby(r)) {
                        log.debug("任务队列已满");
                        abortPolicy_.rejectedExecution(r, p);
                    } else {
                        log.debug("任务已加入备用队列");
                    }
                }
        );
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        return threadPoolExecutor;
    }

    public static void main(String[] args) {
        ThreadPoolExecutor threadPool = defaultThreadPool();
        try {
            threadPool.execute(() -> {
                System.out.println("1--->" + Thread.currentThread().getName());
                ThreadUtil.sleep(3000);
                System.out.println("1<---");
            });

            threadPool.execute(() -> {
                System.out.println("2--->" + Thread.currentThread().getName());
                ThreadUtil.sleep(3000);
                System.out.println("2<---");
            });

            threadPool.execute(() -> {
                System.out.println("3--->" + Thread.currentThread().getName());
                ThreadUtil.sleep(3000);
                System.out.println("3<---");
            });

            threadPool.execute(() -> {
                System.out.println("4--->" + Thread.currentThread().getName());
                ThreadUtil.sleep(3000);
                System.out.println("4<---");
            });

            threadPool.execute(() -> {
                System.out.println("5--->" + Thread.currentThread().getName());
                ThreadUtil.sleep(3000);
                System.out.println("5<---");
            });
/*
            threadPool.execute(()->{
                System.out.println("6--->"+Thread.currentThread().getName());
                ThreadUtil.sleep(60000);
                System.out.println("6<---");
            });

            threadPool.execute(()->{
                System.out.println("7--->"+Thread.currentThread().getName());
                ThreadUtil.sleep(70000);
                System.out.println("7<---");
            });*/
        } catch (Exception e) {
            e.printStackTrace();
        }
        ThreadUtil.sleep(300000);
    }

    private static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        public DefaultThreadFactory(String poolName) {
            if (StrUtil.isBlank(poolName)) namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
            else namePrefix = poolName + "-";
        }

        public DefaultThreadFactory() {
            namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            String name = namePrefix + threadNumber.getAndIncrement();
            log.debug("创建新线程：{}", name);
            Thread t = new Thread(r, name) {
                @Override
                public void run() {
                    try {
                        super.run();
                    } finally {
                        int i = threadNumber.decrementAndGet();
                        log.debug("线程结束：{}，剩余：{}", this.getName(), i - 1);
                    }
                }
            };
            if (t.isDaemon()) t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY);
            t.setUncaughtExceptionHandler((t1, e) -> log.error("线程异常：{}", t1.getName(), e));
            return t;
        }
    }
}
