欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java 多线程学习-----初入线程池

程序员文章站 2022-04-21 09:23:33
...
   使用线程池初衷
   最近项目做代码优化,有一业务功能,大致描述为有20几台分机往总机发送文件,总机在收到文件之后,会往分机反馈数据处理情况。
    这时候想到使用调度来查询总机收到的消息,处理之后分别发送至各分机,为避免排队处理,决定使用多线程,并使用线程池来管理。
    创建线程池
    查找资料之后,采用JDK1.5的java.util.concurrent包创建线程池,本例使用可重用固定线程数的线程池,代码如下:
	private static int maxThreads = 20;	//最大线程数
	private static ExecutorService e = Executors.newFixedThreadPool(maxThreads);	//创建一个有固定数量线程的线程池

    获取任务执行结果
    然后,我使用了FutureTask类来获取任务执行结果,并可以设置任务超时时间,代码如下:
FutureTask<Boolean> ft = new FutureTask<Boolean>(thread,true);
e.execute(ft);
Boolean fTaskResult = ft.get();  //不设置等待时间,等待计算完成,获取结果
//Boolean fTaskResult = ft.get(fTimeout, TimeUnit.SECONDS);  //设置等待时间,时间到了之后,获取任务执行结果,如果超时,则会抛出TimeoutException异常

    如果任务执行超时,我们可以调用FutureTask类的cancel(boolean mayInterruptIfRunning)方法来请求中断任务;
    参数mayInterruptIfRunning - 如果应该中断执行此任务的线程,则为 true;否则允许正在运行的任务运行完成,代码如下:
catch (TimeoutException e) {
		System.out.println("主线程等待计算结果超时,因此中断任务线程");
		ft.cancel(true);
}

   但是,这样存在一个问题,就是在调用FutureTask对象的get()方法时,会阻塞线程,直到任务执行完成,会阻止其他线程的执行,网上搜索无果,就用线程池又打开一个线程执行FutureTask对象的get()方法,代码如下:
	/**
	 * 执行任务
	 * @param t	线程对象
	 */
	public static void beginTask(Thread t){
		e.execute(t);
	}
	
	/**
	 * 不设置超时时间的任务调用
	 * @param key	线程标识
	 * @param t		线程对象
	 */
	public static void beginTask(String key,Thread thread){
		final String fKey = key;
		final FutureTask<Boolean> ft = new FutureTask<Boolean>(thread,true);
		runningTaskMap.put(key, ft);	//存储任务标识
		e.execute(ft);
		
		beginTask(new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub		
				try {			
					Boolean fTaskResult = ft.get();
					if(fTaskResult){
						System.out.println("key为:"+fKey+"的线程执行完成");						
					}
				} catch (InterruptedException e) {
					System.out.println("主线程在等待计算结果时被中断");
				} catch (ExecutionException e) {
					System.out.println("主线程等待计算结果,但计算抛出异常");
				}finally{
					runningTaskMap.remove(fKey);	//移除线程标识
				}
			}
		});
	}

   任务标记
   另外,为了能够获取正在执行的任务,我们对每个任务做了标记,存放在map集合中,代码如下:
	//创建一个带有标识的任务集合
	private static Map<String, FutureTask<Boolean>> runningTaskMap = Collections.synchronizedMap(new HashMap<String, FutureTask<Boolean>>());

   当每次调用线程池,就会往map集合中存放记录,调用完之后,再移除掉。
   我们也可以通过标识来中断正在执行的任务,代码如下:
	/**
	 * 根据传入的任务标识,来中断对应任务
	 * @param key	任务标识
	 */
	public static void intteruptTask(String key){
		FutureTask<Boolean> ft = runningTaskMap.get(key);
		ft.cancel(true);
		if(ft.isDone()){
			runningTaskMap.remove(key);
		}
	}

   最后附上完整代码:
package task.concurrent.threadPoor;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class FixedThreadPool {
	private static int maxThreads = 20;	//最大线程数
	private static ExecutorService e = Executors.newFixedThreadPool(maxThreads);	//创建一个有固定数量线程的线程池
	
	//创建一个带有标识的任务集合
	private static Map<String, FutureTask<Boolean>> runningTaskMap = Collections.synchronizedMap(new HashMap<String, FutureTask<Boolean>>());
	
	/**
	 * 执行任务
	 * @param t	线程对象
	 */
	public static void beginTask(Thread t){
		e.execute(t);
	}
	
	/**
	 * 不设置超时时间的任务调用
	 * @param key	线程标识
	 * @param t		线程对象
	 */
	public static void beginTask(String key,Thread thread){
		final String fKey = key;
		final FutureTask<Boolean> ft = new FutureTask<Boolean>(thread,true);
		runningTaskMap.put(key, ft);	//存储任务标识
		e.execute(ft);
		
		beginTask(new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub		
				try {			
					Boolean fTaskResult = ft.get();
					if(fTaskResult){
						System.out.println("key为:"+fKey+"的线程执行完成");						
					}
				} catch (InterruptedException e) {
					System.out.println("主线程在等待计算结果时被中断");
				} catch (ExecutionException e) {
					System.out.println("主线程等待计算结果,但计算抛出异常");
				}finally{
					runningTaskMap.remove(fKey);	//移除线程标识
				}
			}
		});
	}
	
	/**
	 * 设置有超时时间的任务调用
	 * @param key		线程标识
	 * @param thread	线程对象
	 * @param timeout	秒
	 */
	public static void beginTask(String key, Thread thread, int timeout) {
		final String fKey = key;
		final int fTimeout = timeout;
		final FutureTask<Boolean> ft = new FutureTask<Boolean>(thread,true);
		runningTaskMap.put(key, ft);	//存储任务标识
		e.execute(ft);
		
		beginTask(new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub		
				try {			
					Boolean fTaskResult = ft.get(fTimeout, TimeUnit.SECONDS);
					if(fTaskResult){
						System.out.println("key为:"+fKey+"的线程执行完成");						
					}
				} catch (InterruptedException e) {
					System.out.println("主线程在等待计算结果时被中断");
				} catch (ExecutionException e) {
					System.out.println("主线程等待计算结果,但计算抛出异常");
				} catch (TimeoutException e) {
					System.out.println("主线程等待计算结果超时,因此中断任务线程");
					ft.cancel(true);
				}finally{
					runningTaskMap.remove(fKey);	//移除线程标识
				}
			}
		});
	}
	
	/**
	 * 根据传入的任务标识,来中断对应任务
	 * @param key	任务标识
	 */
	public static void intteruptTask(String key){
		FutureTask<Boolean> ft = runningTaskMap.get(key);
		ft.cancel(true);
		if(ft.isDone()){
			runningTaskMap.remove(key);
		}
	}
	
	/**
	 * 关闭线程池
	 */
	public static void shutDownPool(){
		e.shutdown();
	}
}


   总结:调用及测试代码就不贴出来了,由于是初写线程池代码,有很多不足之处,望网友能够指出,相互学习进步。
   参考资料:http://blog.csdn.net/hemingwang0902/article/details/4557304
http://westyi.iteye.com/blog/714935
不积跬步无以至千里