java 线程池的使用

/**
 *  任务的缓存队列  workQueue
 *  workQueue 的类型为BlockingQueue<Runnable> ,通常可以取下面三种类型:
 *  1),有界任务队列ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
 *  2),无界任务队列LinkedBlockingQueue:基于连标的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE
 *  3),直接提交队列synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来直接新的任务。
 */


/**
 * 拒绝策略  RejectedExecutionHandler (new ThreadPoolExecutor.AbortPolicy)
 * AbortPolicy:丢弃任务并抛出RejectedExecutionException
 * CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是任务提交线程的性能极有可能会急剧下降。
 * DiscardOldestPolicy:丢弃队列中最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
 * DiscardPolicy:丢弃任务,不做任何处理。
 */

/**
 * 线程池任务处理策略
 * 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务。
 * 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,
 *      若添加成功,则该任务会等待空闲线程将其去出去执行;
 *      若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
 * 如果当前线程池中的数目达到maximumPoolSize,则会采取任务拒绝策略进行处理。
 * 如果线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;
 * 如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
 *
 *
 */


/**
 * 线程池的关闭
 * ThreadPoolExecutor提供了连个方法。用于线程池的关闭。分别是shutdown()和shutdownNow(),
 * showdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完成后才终止,但再也不会接受新的任务。
 * showdown():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。

 */


/**
 *
 * @param corePoolSize  表示运行线程池中允许同时运行的最大线程数
 * @param maximumPoolSize 线程池运行的最大的线程数,它表示最大能创建多大的线程,maximumPoolSize 肯定大于等于corePoolSize
 * @param keepAliveTime 表示线程没有任务时最多保持多久然后停止。默认情况下,只有线程池中线程数大于corePoolSize时,keepAliveTime才会起作用。
 * @param unit  keepAliveTime 的单位。
 * @param workQueue  一个阻塞队列,用来存储等待执行的任务,当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。通过workQueue,线程实现了阻塞功能
 * @param ThreadFactory 线程工厂,用来创建线程
 * @param handler 表示当拒绝处理任务时的策略
 */
public static ExecutorService newThreadPool(int corePoolSize,int maximumPoolSize, long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler){
    return new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime, unit,workQueue,handler);
}


/**
 * 如何选择线程池数量
 * 线程池大小决定着系统的性能,过大或者过小的线程池数量都无法发挥最优的系统性能
 * 当然线程池的大小也不需要做的太过于精确,只需要避免过大和过小的情况,
 * 一般来说,确定线程池的大小需要考虑cpu的数量,内存大小,任务是计算密集型还是IO密集型等因素
 *
 * NCPU = CPU的数量
 * UCPU = 期望对CPU的使用率 0 <= UCPU <= 1
 * W/C  = 等待时间与计算时间的比率
 * 如果希望处理器达到理想的使用率。那么线程池最优大小为:
 * 线程池大小 = NCPU*UCPU(1+W/C)
 */


/**
 * 线程池工厂
 * Executors的线程池如果不指定线程工厂会使用Executors的DefaultThreadFactory,默认线程池工厂创建的线程都是非守护线程。
 * 使用自定义的线程工厂可以做很多事情,比如可以跟踪线程池在合适创建了多少线程,也可以自定义线程名称和优先级。
 * 如果将新建的线程都设置成守护线程,当主线程退出后,将会强制销毁线程池
 *
 *
 */

public static class ThreadFactoryDemo{

    public  static class MyTask1 implements Runnable{

        @Override
        public void run() {
            System.out.println(System.currentTimeMillis()+"Thread ID:"+Thread.currentThread().getId());
            try{
                Thread.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {
        MyTask1 task = new MyTask1();
        ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                t.setDaemon(true);
                System.out.println("创建线程"+t);
                return t;
            }
        });
        for(int i = 0;i<=4;i++){
            es.submit(task);
        }
    }
}

/**
 * 手动创建线程池有几个注意点
 * 1,任务独立。如何任务依赖于其他任务,那么可能产生死锁。例如某个任务等待另一个任务的返回值或执行结果,那么除非线程池足够大,否则将发生线程饥饿死锁。
 * 2,合理配置阻塞时间过长的任务。如果任务阻塞时间过长,那么即使不出现死锁,线程池的性能也会变得很糟糕。在java并发包里可阻塞方法都
 *    同时定义了限时方式和不限时方式。例如:
 *        Thread.join,BlockingQueue.put,CountDownLatch.await等,如果任务超时,则标识任务失败,然后中止任务或者将任务放回队列以便随后执行,
 *        这样,无论任务的最终结果是否成功,这种办法都能够保证任务总能继续执行下去。
 * 3,设置合理的线程池大小。只需要避免过大或者过小的情况即可,上文的公式线程池大小 = NCPU * UCPU(1+W/C)
 * 4,选择合适的阻塞队列。newFixedThreadPool 和 newSingleThreadExecutor都使用了无界的阻塞队列,无界阻塞队列会有消耗很大的内存,
 *    如果使用了有界阻塞队列,他会规避内存占用过大的问题,但是当任务填满有界队列,新的任务怎么办?在使用有界队列时,需要选择合适的拒绝策略,队列的大小和线程池大小必须疫情调节。
 *    对于非常大的或者无界的线程池可以使用SynchronousQueue来避免任务排队,以直接将任务从生产者提交到工作者线程。
 */
/**
 * 下面是Thrift框架处理socket任务所使用的一个线程池,可以看一下FaceBook的工程师是如何自定义线程池的。
 * @param args
 * @return
 */
private static ExecutorService createDefaultExecutorService(Args args) {
    SynchronousQueue executorQueue = new SynchronousQueue();

    return new ThreadPoolExecutor(args.minWorkerThreads, args.maxWorkerThreads, 60L, TimeUnit.SECONDS,
            executorQueue);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值