Java线程池的使用

设置线程池的大小

线程池的理想大小取决于被提交任务的类型以及所部署的系统。通常根据某种配置机制来提供。

对于计算密集型的任务,在拥有N_{cpu}个处理器的系统上,当线程池的大小为N_{cpu+1}时,通常能实现最优的利用率。

对于包含I/O操作或者其他阻塞操作的任务,由于线程并不会一直执行,因此线程池的规模应该更大。

                                     N_{cpu}=number of CPUs

                                     U_{cpu}=target CPU utilization, 0 ≤U_{cpu}≤1

                                   \frac{W}{C}=ratio of wait time to compute time

要使处理器达到期望的使用率,线程池的最优大小等于:

                               N_{threads}=N_{cpus}*U_{cpu}*(1+\frac{W}{C})

可以通过Runtime获得CPU的数目:

int N_CPU = Runtime.getRuntime().availableProcessors();

配置ThreadPoolExecutor

ThreadPoolExecutor为一些Executor提供了基本的实现:Executor框架与Java线程池

饱和策略

当有界队列被填满后,饱和策略开始发挥作用。通过setRejectedExecutionHandler来修改。

AbortPolicy: 默认策略,抛出未检查的RejectedExecutionException供调用者捕获。

DiscardPolicy: 悄悄抛弃该任务。

DiscardOldestPolicy: 抛弃下一个将被执行的任务(对于优先队列,将是优先级最高的那个任务),然后尝试重新提交新的任务。

CallerRunsPolicy: 既不抛弃任务,也不抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。当工作队列被填满后,下一个任务会在调用execute时在主线程中执行。由于执行任务需要一定的时间,因此主线程至少在一段时间内不能提交任务,从而使得工作者线程有时间来处理完正在执行的任务。

 示例代码:

ThreadPoolExecutor executor = new ThreadPoolExecutor(N_THREADS, N_THREADS, 0L,
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(CAPACITY));
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

使用Semaphore限制任务的到达率:

public class BoundedExecutor {
    private final Executor exec;
    private final Semaphore semaphore;

    public BoundedExecutor(Executor exec, int bound) {
        this.exec = exec;
        this.semaphore = new Semaphore(bound);
    }

    public void submitTask(final Runnable command) throws InterruptedException {
        semaphore.acquire();
        try {
            exec.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        command.run();
                    } finally {
                        semaphore.release();
                    }
                }
            });
        } catch (Exception e) {
            semaphore.release();
        }
    }
}

每当线程需要创建一个线程时,都是通过线程工厂方法来完成的。可以为每个线程指定UncaughtExceptionHandler,或者实例化一个定制的Thread类用于执行调试信息的记录:

public class MyThreadFactory implements ThreadFactory {
    private final String poolName;

    public MyThreadFactory(String poolName) {
        this.poolName = poolName;
    }

    @Override
    public Thread newThread(Runnable r) {
        return new MyAppThread(r, poolName);
    }
}

public class MyAppThread extends Thread {
    public static final String DEFAULT_NAME = "MyAppThread";
    private static final AtomicInteger created = new AtomicInteger();
    private static final AtomicInteger alive = new AtomicInteger();
    private static final Logger logger = Logger.getAnonymousLogger();
    private static volatile boolean debugLifeCycle = false;

    public MyAppThread(Runnable target) {
        this(target, DEFAULT_NAME);
    }

    public MyAppThread(Runnable target, String name) {
        super(target, name + "-" + created.incrementAndGet());
        setUncaughtExceptionHandler(
                new Thread.UncaughtExceptionHandler() {
                    @Override
                    public void uncaughtException(Thread t, Throwable e) {
                        logger.log(Level.SEVERE, "UNCAUGHT in thread " + t.getName(), e);
                    }
                }
        );
    }

    @Override
    public void run() {
        //复制debug标志以确保一致的值??
        boolean debug = debugLifeCycle;
        if (debug) logger.log(Level.FINE, "Created " + getName());
        try {
            alive.incrementAndGet();
            super.run();
        } finally {
            alive.decrementAndGet();
            if (debug) logger.log(Level.FINE, "Exiting " + getName());
        }
    }
    
    public static int getThreadCreated() { return created.get();}
    public static int getThreadAlive() { return alive.get();}
    public static boolean getDebug() { return debugLifeCycle; }
    public static void setDebug(boolean debug) { debugLifeCycle = debug;}
}

扩展ThreadPoolExecutor

ThreadPoolExecutor是可以扩展的,它提供了几个可以在子类中改写的方法:beforeExecute、afterExecute和terminated,这些方法可以用于扩展ThreadPoolExecutor的行为。

示例代码:

public class TimingThreadPool extends ThreadPoolExecutor {
    private final ThreadLocal<Long> startTime = new ThreadLocal<Long>();
    private final Logger logger = Logger.getLogger("TimingThreadPool");
    private final AtomicLong numTasks = new AtomicLong();
    private final AtomicLong totalTime = new AtomicLong();

    public TimingThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        logger.fine(String.format("Thread %s: start %s", t, r));
        startTime.set(System.nanoTime());
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        try {
            long endTime = System.nanoTime();
            long taskTime = endTime - startTime.get();
            numTasks.incrementAndGet();
            totalTime.addAndGet(taskTime);
            logger.fine(String.format("Thread %s: end %s, time = %dns", t, r, taskTime));
        } finally {
            super.afterExecute(r, t);
        }
    }

    @Override
    protected void terminated() {
        try {
            logger.info(String.format("Terminated: avg time=%dns", totalTime.get() / numTasks.get()));
        } finally {
            super.terminated();
        }
    }
}

参考:《Java并发编程实战》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值