java并发编程–线程池初步

作者: admin 分类: 学习文档 发布时间: 2011-11-07 17:12

服务器应用程序经常需要处理执行时间很短而数目巨大的请求, 如果为每一个请求创建一个新的线程, 会导致一些问题的出现, 如:

1. 性能瓶颈. 线程的创建和销毁需要执行大量的后台操作, 如果单个请求的执行时间很短, 有可能花在创建和销毁线程上的时间大于真正执行请求的时间.

2. 可能会导致资源不足. 大量的并发请求意味着需要创建大量的线程, 过多的线程存在会吞噬大量的系统资源, 而且CPU需要在这些线程间不断切换, 这可能引发”切换过度”的问题.

为了适应上述场合, java在JDK1.5中引入了线程池的概念. 线程池中存放着一定数量的已创建好的线程, 当一个请求到来时, 只需从线程池中取出一个线程来执行请求, 请求完成后再将线程归还给线程池. 同时, 我们可以为线程池指定最大的线程数量, 当池中所有线程都处于活动状态下, 新的任务会排队等候, 直到之前的某个任务处理完成后, 新的任务才能得到处理.

 

创建线程池. java.util.concurrent.Executors类提供了多个静态方法用于创建线程池.

|–public static ExecutorService newFixedThreadPool(int nThreads): 创建一个可重用的固定线程数的线程池. 如果池中所有的nThreads个线程都处于活动状态时提交任务(任务通常是Runnable或Callable对象), 任务将在队列中等待, 直到池中出现可用线程.

|–public static ExecutorService newCachedThreadPool(): 调用此方法创建的线程池可根据需要自动调整池中线程的数量. 执行任务时将重用存在先前创建的线程(如果池中存在可用线程的话). 如果池中没有可用线程, 将创建一个新的线程, 并将其添加到池中. 池中的线程超过60秒未被使用就会被销毁, 因此长时间保持空闲的CachedThreadPool不会消耗额外的资源.

|–public static ExecutorService newSingleThreadExecutor(): 创建一个单线程的Executor. 这个Executor保证按照任务提交的顺序依次执行任务.

|–public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize): 创建一个可重用的固定线程数的线程池. ScheduledExecutorService是ExecutorService的子接口, 调用ScheduledExecutorService的相关方法, 可以延迟或定期执行任务.

以上静态方法均使用默认的ThreadFactory(即Executors.defaultThreadFactory()方法的返回值)创建线程, 如果想要指定ThreadFactory, 可调用他们的重载方法.通过指定ThreadFactory, 可以定制新建线程的名称, 线程组, 优先级, 守护线程状态等.

如果Executors提供的创建线程池的方法无法满足要求, 可以使用ThreadPoolExecutor类创建线程池.

 

提交任务. 所有的线程池都是ExecutorService及其子类的对象, 因此, 可以调用ExecutorService的相关方法提交任务.

|–void execute(Runnable command): 使用池中已存在的线程或新建一个线程执行command.

 

Java代码 复制代码 收藏代码
  1. public class ExecutorsDemo {
  2. public static void main(String[] args) throws Exception {
  3. System.out.println(“—————-FixedThreadPool———————”);
  4. ExecutorService fixedPool = getFixedThreadPool();
  5. executeThread(fixedPool);
  6. // 为了避免混淆, 需要等待executeThread(fixedPool)执行完成
  7. Thread.sleep(3000);
  8. System.out.println(“—————-CashedThreadPool———————”);
  9. ExecutorService cashedPool = getCashedThreadPool();
  10. executeThread(cashedPool);
  11. // 为了避免混淆, 需要等待executeThread(cashedPool)执行完成
  12. Thread.sleep(3000);
  13. System.out.println(“—————-SingleThreadExecutor———————”);
  14. ExecutorService singleExecutor = getSingleThreadExecutor();
  15. executeThread(singleExecutor);
  16. }
  17. // 只存在一个线程的线程池
  18. private static ExecutorService getSingleThreadExecutor() {
  19. return Executors.newSingleThreadExecutor();
  20. }
  21. // 创建一个根据需要自动调整大小的线程池
  22. private static ExecutorService getCashedThreadPool() {
  23. return Executors.newCachedThreadPool();
  24. }
  25. // 创建一个可重用的固定线程数的线程池
  26. private static ExecutorService getFixedThreadPool() {
  27. return Executors.newFixedThreadPool(2);
  28. }
  29. private static void executeThread(ExecutorService pool) {
  30. Thread t1 = new MyThread();
  31. Thread t2 = new MyThread();
  32. Thread t3 = new MyThread();
  33. Thread t4 = new MyThread();
  34. // 将Tread放入线程池中执行
  35. // MyThread类继承自Thread, 而Thread类实现了Runnable接口
  36. pool.execute(t1);
  37. pool.execute(t2);
  38. pool.execute(t3);
  39. pool.execute(t4);
  40. // 关闭线程池
  41. pool.shutdown();
  42. }
  43. private static final class MyThread extends Thread {
  44. @Override
  45. public void run() {
  46. super.run();
  47. System.out.println(Thread.currentThread().getName() + “: is running!”);
  48. }
  49. }
  50. }
public class ExecutorsDemo {

	public static void main(String[] args) throws Exception {
		System.out.println("----------------FixedThreadPool---------------------");
		ExecutorService fixedPool = getFixedThreadPool();
		executeThread(fixedPool);

		// 为了避免混淆, 需要等待executeThread(fixedPool)执行完成
		Thread.sleep(3000);

		System.out.println("----------------CashedThreadPool---------------------");
		ExecutorService cashedPool = getCashedThreadPool();
		executeThread(cashedPool);

		// 为了避免混淆, 需要等待executeThread(cashedPool)执行完成
		Thread.sleep(3000);

		System.out.println("----------------SingleThreadExecutor---------------------");
		ExecutorService singleExecutor = getSingleThreadExecutor();
		executeThread(singleExecutor);

	}

	// 只存在一个线程的线程池
	private static ExecutorService getSingleThreadExecutor() {
		return Executors.newSingleThreadExecutor();
	}

	// 创建一个根据需要自动调整大小的线程池
	private static ExecutorService getCashedThreadPool() {
		return Executors.newCachedThreadPool();
	}

	// 创建一个可重用的固定线程数的线程池
	private static ExecutorService getFixedThreadPool() {
		return Executors.newFixedThreadPool(2);
	}

	private static void executeThread(ExecutorService pool) {
		Thread t1 = new MyThread();
		Thread t2 = new MyThread();
		Thread t3 = new MyThread();
		Thread t4 = new MyThread();
		// 将Tread放入线程池中执行
		// MyThread类继承自Thread, 而Thread类实现了Runnable接口
		pool.execute(t1);
		pool.execute(t2);
		pool.execute(t3);
		pool.execute(t4);
		// 关闭线程池
		pool.shutdown();
	}

	private static final class MyThread extends Thread {
		@Override
		public void run() {
			super.run();
			System.out.println(Thread.currentThread().getName() + ": is running!");
		}
	}
}

 

程序的输出为:

 

—————-FixedThreadPool———————

pool-1-thread-1: is running!

pool-1-thread-2: is running!

pool-1-thread-2: is running!

pool-1-thread-1: is running!

—————-CashedThreadPool———————

pool-2-thread-1: is running!

pool-2-thread-2: is running!

pool-2-thread-4: is running!

pool-2-thread-3: is running!

—————-SingleThreadExecutor———————

pool-3-thread-1: is running!

pool-3-thread-1: is running!

pool-3-thread-1: is running!

pool-3-thread-1: is running!

 

 

|–Future<T> submit(Callable<T> task): 使用池中已存在的线程或新建一个线程执行task, 与execute()方法不同的是, 该方法会返回线程的执行结果. submit方法接受一个Callable<T>对象, Callable<T>接口是一个泛型接口, 实现Callable<T>接口需要重写其中的call()方法, call()方法将返回一个T对象. submit方法的返回值是Future<T>对象, 调用该对象的get()可以获得call()方法的返回值.

 

Java代码 复制代码 收藏代码
  1. public class FutureDemo {
  2. public static void main(String[] args) throws Exception {
  3. ExecutorService pool = Executors.newFixedThreadPool(2);
  4. Future<Integer> intFuture = pool.submit(new IntegerCallable());
  5. // get()方法将阻塞主线程, 直到IntegerCallable线程的call()运行结束并返回结果时为止.
  6. Integer returnInt = intFuture.get();
  7. System.out.println(“返回值为” + returnInt);
  8. Future<Boolean> boolFuture = pool.submit(new BooleanCallable());
  9. Boolean returnBool = boolFuture.get();
  10. System.out.println(“返回值为” + returnBool);
  11. pool.shutdown();
  12. }
  13. private final static class IntegerCallable implements Callable<Integer> {
  14. // call()方法的返回值类型由泛型决定
  15. @Override
  16. public Integer call() throws Exception {
  17. return 2;
  18. }
  19. }
  20. private final static class BooleanCallable implements Callable<Boolean> {
  21. @Override
  22. public Boolean call() throws Exception {
  23. return true;
  24. }
  25. }
  26. }
public class FutureDemo {
	public static void main(String[] args) throws Exception {
		ExecutorService pool = Executors.newFixedThreadPool(2);

		Future<Integer> intFuture = pool.submit(new IntegerCallable());
                // get()方法将阻塞主线程, 直到IntegerCallable线程的call()运行结束并返回结果时为止.
		Integer returnInt = intFuture.get();
		System.out.println("返回值为" + returnInt);

		Future<Boolean> boolFuture = pool.submit(new BooleanCallable());
		Boolean returnBool = boolFuture.get();
		System.out.println("返回值为" + returnBool);

		pool.shutdown();
	}

	private final static class IntegerCallable implements Callable<Integer> {
		// call()方法的返回值类型由泛型决定  
		@Override
		public Integer call() throws Exception {
			return 2;
		}
	}

	private final static class BooleanCallable implements Callable<Boolean> {
		@Override
		public Boolean call() throws Exception {
			return true;
		}
	}
}

程序的输出结果为:

返回值为2

返回值为true

|–List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks): 批量执行多个任务.

 

Future类. 如果需要获取线程的执行结果, 那么就会使用到Future. Future对象是一个指向异步执行结果的引用, 由于线程的异步特性, Future对象在其创建之初可能并不可用, 比如线程的call()方法尚未完成时. 可以调用Future对象的isDone()方法判断线程结果是否已经可用, 在线程结果返回之前调用Future对象的get()方法, 将导致阻塞.

 

关闭线程池. 使用完线程池后需要关闭它, 否则程序可能一直处于运行状态. ExecutorService提供了2个方法用于关闭线程池:

|–void shutdown(): 关闭线程池, 不再接受新任务. 如果存在正在执行的任务, 则等待任务执行完成.

|–List<Runnable> shutdownNow(): 关闭线程池, 不再接受新任务. 尽力尝试停止正在执行的任务, 并返回正在等待的任务列表.

|–boolean isShutdown(): 判断线程池是否已经关闭.

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Protected by WP Anti Spam