解决Java线程池任务执行完毕后线程回收问题


http://www.cnblogs.com/pengineer/p/5011965.html
      对于经常使用第三方框架进行web开发的程序员来说,Java线程池理所应当是非常智能的,线程的生命周期应该完全由Java本身控制,我们要做的就是添加任务和执行任务。但是,最近做文档批量上传同步时发现线程池中的所有任务执行完毕后,线程并没有停止,然后做了一个测试,发现确实如此:
 
问题及现象:
 
     public static void method1() {
          BlockingQueue queue = new LinkedBlockingQueue();
          ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 10, TimeUnit.SECONDS, queue);   
          for ( int i = 0; i < 20; i++) {
              executor.execute( new Runnable() {
                  public void run() {
                      try {
                          System. out.println( this.hashCode()/1000);
                            for ( int j = 0; j < 10; j++) {
                               System. out.println( this.hashCode() + ":" + j);
                               Thread.sleep(this.hashCode()%2);
                          } 
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System. out.println(String. format("thread %d finished", this.hashCode()));
                  }
              });
          }
      }
debug模式下,任务执行完后,显示的效果如下:


也就是说,任务已经执行完毕了,但是线程池中的线程并没有被回收。但是我在ThreadPoolExecutor的参数里面设置了超时时间的,好像没起作用,原因如下:
工作线程回收需要满足三个条件:
1)  参数allowCoreThreadTimeOut为true


2)  该线程在keepAliveTime时间内获取不到任务,即空闲这么长时间


3)  当前线程池大小 > 核心线程池大小corePoolSize。
 
解决一:
 
      public static void method1() {
          BlockingQueue queue = new LinkedBlockingQueue();
          ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS,queue);  
          executor.allowCoreThreadTimeOut(true);
          for ( int i = 0; i < 20; i++) {
              executor.execute( new Runnable() {
                  public void run() {
                      try {
                           System. out.println( this.hashCode()/1000);
                              for ( int j = 0; j < 10; j++) {
                                System. out.println( this.hashCode() + ":" + j);
                                Thread. sleep(this.hashCode()%2);
                           } 
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System. out.println(String. format("thread %d finished", this.hashCode()));
                  }
              });
          }
      }
需要注意的是,allowCoreThreadTimeOut 的设置需要在任务执行之前,一般在new一个线程池后设置;在allowCoreThreadTimeOut设置为true时,ThreadPoolExecutor的keepAliveTime参数必须大于0。
 
 
解决二:
 
     public static void method1() {
          BlockingQueue queue = new LinkedBlockingQueue();
          ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, queue);
          for ( int i = 0; i < 20; i++) {
              executor.execute( new Runnable() {
                  public void run() {
                      try {
                          System. out.println( this.hashCode()/1000);
                            for ( int j = 0; j < 10; j++) {
                               System. out.println( this.hashCode() + ":" + j);
                               Thread. sleep(this.hashCode()%2);
                          } 
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System. out.println(String. format("thread %d finished", this.hashCode()));
                  }
              });
          }         
          executor.shutdown();
      }
在任务执行完后,调用shutdown方法,将线程池中的空闲线程回收。该方法会使得keepAliveTime参数失效。

<< ### 单线程的基本原理: 单线程是指程序中只有一个执行路径(即主线程),所有的任务都按照顺序逐一完成。在这种模式下,当前的任务必须完全完成后才能开始下一个任务。 **解释**: 在单线程环境中,所有指令都是按部就班地进行处理的。如果某个操作耗时较长(例如I/O等待或计算密集型任务),整个应用会处于阻塞状态,直到该操作结束才会继续往下运行其他任务。 ```java public class SingleThreadExample { public static void main(String[] args) throws InterruptedException{ System.out.println("Task A"); Thread.sleep(2000); //模拟耗时任务 System.out.println("Task B"); } } ``` --- ### 多线程的基本原理: 多线程允许同一时间内有多个线程并发执行。每个线程都可以独立于其他线程运行,并且它们共享同一个进程中的资源和内存空间。 **解释**: 当涉及到复杂的、时间敏感的应用场景时,使用多线程能够显著提高效率。比如,在GUI界面开发过程中,可以让UI保持响应的同时后台执行长时间运算;又如网络服务器端接收大量客户端连接请求后同时为每一个客户提供服务等。 以下是一段简单的创建并启动两个线程的例子: ```java class MyThread extends Thread { @Override public void run() { for (int i = 1; i <=5 ;i++) { try { sleep(100); System.out.println(Thread.currentThread().getName()+" "+i); } catch (InterruptedException e) {} } } } public class MultiThreadExample { public static void main(String[] args){ new MyThread().start(); // 启动第一个线程 new MyThread().start(); // 启动第二个线程 } } ``` 上述代码将产生交替输出的结果表明了这两个线程是在“同时”工作的事实。 --- ### Java线程池的基本原理及优势: 线程池是一种管理和重用固定数量的工作线程的方法,避免频繁创建销毁带来的开销。通过预先初始化若干个线程放入"池子"里待命,当任务到来的时候从这些已有的空闲线程选取出来执行而不是每次都重新生成新的实例再回收垃圾收集器管理废弃的对象,从而达到节省系统性能的目的. **核心组件包括以下几个方面**: 1. **Executor接口:** 提供了一个简单的方式来定义任务提交与如何以及何时被执行之间的解耦机制. 2. **ThreadPoolExecutor类:** 它实现了executor接口的具体细节, 包含构造函数参数用于配置最小/最大pool size , keep alive time等等设置项 . 3. **Callable/Future 接口:** Callable类似于runnable但是它可以返回结果并且可能抛出异常,Futures代表异步计算的结果. 示例 - 使用Executors工厂方法构建一个固定大小的线程池: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); Runnable worker = ()->System.out.println(Thread.currentThread().getName()); for(int i=0;i<4;i++) executor.submit(worker); executor.shutdown(); } } ``` 在这个例子中我们建立了包含两成员工作单元组成的队列然后依次添加四个作业进去让他们轮流被调度去履行职责直至全部完毕为止才关闭这个设施不再接受任何新来的使命要求.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值