第七章:并发编程-线程池的使用

线程饥饿死锁

在线程池中,如果任务依赖于其他任务,那么可能产生死锁。

public class ThreadDeadlock {
	
	ExecutorService executorService = Executors.newSingleThreadExecutor();
	
	public class RenderPageTask implements Callable<String>{

		@Override
		public String call() throws Exception {
			Future<String> header,footer;
			header = executorService.submit(new LoadFile());
			footer = executorService.submit(new LoadFile());
			
			return header.get() + footer.get();
		}
		
		class LoadFile implements Callable<String>{

			@Override
			public String call() throws Exception {
				return null;
			}
			
		}
		
	}
	
}

运行时间较长的任务:

如果任务阻塞的时间过长,那么即使不出现死锁,线程池的响应性也会变得糟糕。

有一项技术可以缓解执行时间较长任务造成的影响,即是限定任务等待资源的时间,而不是无限制地等待。


设置线程池的大小

分析计算环境、资源预算和任务的特征。在部署的系统中有多少个CPU?多大的内存?任务是计算密集型还是IO密集型还是两者都有?

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

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


ThreadPoolExecutor的构造函数:

ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue,
                        ThreadFactory threadFactory,
                        RejectedExecutionHandler handler)

线程的创建与销毁

    线程池的基本大小corePoolSize,最大大小maximumPoolSize以及存活时间keepAliveTime等因素共同负责线程的创建与销毁。

    基本大小就是线程池的目标大小,即在没有任务执行时线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。

    最大大小表示可以同时活动的线程数量的上限。

    存活时间:如果某个线程的空闲时间超过了存活时间,那么将线程标记为可回收的,并且当线程池的当前大小超过了基本大小时,这个线程将被终止。

    当任务相互独立时,为线程池或者工作队列设置界限才是合理的,如果任务之间存在依赖性,那么有界的线程池或队列就可能导致线程饥饿死锁问题。此时应该使用无界的线程池,如newCachedThreadPool。

饱和策略

ThreadPoolExecutor的饱和策略可以通过setRejectedExecutionHandler来修改。

JDK提供了几种不同的RejectedExecutionHandler的实现,如AbortPolicy,CallerRunPolicy,DiscardPolicy,DiscardOldestPolicy。

终止Abort 策略是默认的饱和策略,该策略将抛出未检查的RejiectedExecutionException。

调用者运行Caller-Runs策略实现了一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。

		ThreadPoolExecutor executor = new ThreadPoolExecutor
				(10, 100, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(10));
		//调用者允许实现饱和策略
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

抛弃策略Discard会悄悄抛弃该任务

抛弃最旧的Discard-Oldest策略会抛弃下一个将被执行的任务,然后尝试重新提交该任务。

线程工厂

每当线程池需要创建一个线程,都通过线程工厂方法来完成的。默认的线程工厂方法会创建一个新的、非守护的线程。

public interface ThreadFactory
{

    public abstract Thread newThread(Runnable runnable);
}

自定义线程工厂:

public class MyThreadFactory implements ThreadFactory{
	
	private final String poolName;
	
	public MyThreadFactory(String poolName) {
		super();
		this.poolName = poolName;
	}


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

}
public class MyAppThread extends Thread{
	
	public static final String DEFAULT_NAME = "MyAppThread";
	
	private static volatile boolean debugLifeCycle = false;
	
	private static final AtomicInteger created = new AtomicInteger();
	
	private static final AtomicInteger alive = new AtomicInteger();
	
	private static final Logger LOGGER = Logger.getAnonymousLogger();
	
	public MyAppThread(Runnable r){
		this(r,DEFAULT_NAME);
	}
	
	public MyAppThread(Runnable runnable,String name){
		super(runnable,name+"-"+created.incrementAndGet());
		setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
			
			@Override
			public void uncaughtException(Thread thread, Throwable throwable) {
				LOGGER.log(Level.SEVERE, "UNCAUGT in thread "+thread.getName(), throwable);
			}
		});
	}

	public void run(){
		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 getThreadsCreated(){
		return created.get();
	}
	
	public static int getThreadsAlive(){
		return alive.get();
	}
	
	public static boolean getDebug(){
		return debugLifeCycle;
	}
	
	public static void setDebug(boolean b){
		debugLifeCycle = b;
	}
}

扩展ThreadPoolExecutor

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

	private final ThreadLocal<Long> startTime = new ThreadLocal<>();
	
	private final Logger log = Logger.getLogger("TimingThreadPool");
	
	private final AtomicLong numTasks = new AtomicLong();
	
	private final AtomicLong totalTime = new AtomicLong();
	
	protected void beforeExecute(Thread t,Runnable r){
		super.beforeExecute(t, r);
		log.fine("before");
		startTime.set(System.nanoTime());
	}
	
	protected void afterExecute(Runnable r,Throwable t){
		try {
			long endTime = System.nanoTime();
			long taskTime = endTime - startTime.get();
			numTasks.incrementAndGet();
			totalTime.addAndGet(taskTime);
			log.fine("after");
		} finally {
			super.afterExecute(r, t);
		}
	}
	
	protected void terminated(){
		try {
			log.fine("terminate");
		} finally {
			super.terminated();
		}
	}

}

猜你喜欢

转载自blog.csdn.net/dxh0823/article/details/80041045