论坛首页 入门技术论坛

ExecutorService,也不是想象中的神

浏览 11778 次
该帖已经被评为新手帖
作者 正文
   发表时间:2010-03-30   最后修改:2010-04-01

 

关于ExecutorService好用的方面就不说了,effective java里面是强烈推荐使用Executor代替自己管理Thread。

e.g.

 

	public static void startReceiver() {
		ExecutorService pool = Executors.newFixedThreadPool(rec_thread_count);
		pool.execute(new MsgQReceiver());
	}
 

 

下面看看今天我郁闷的地方:

 

 1.JDK doc里面描述的线程池关闭方法:先暂停接受新任务进来,然后terminate运行中的线程。看起来确实是一个非常完美的方案。

 

void shutdownAndAwaitTermination(ExecutorService pool) {
   pool.shutdown(); // Disable new tasks from being submitted
   try {
     // Wait a while for existing tasks to terminate
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
       pool.shutdownNow(); // Cancel currently executing tasks
       // Wait a while for tasks to respond to being cancelled
       if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
     }
   } catch (InterruptedException ie) {
     // (Re-)Cancel if current thread also interrupted
     pool.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();
   }
 }

 

2.分析里面的shutdownNow()实现,发现居然是用的thread.interrupt()。

 

    public List<Runnable> shutdownNow() {
        /*
         * shutdownNow differs from shutdown only in that
         * 1. runState is set to STOP,
         * 2. all worker threads are interrupted, not just the idle ones, and
         * 3. the queue is drained and returned.
         */
	SecurityManager security = System.getSecurityManager();
	if (security != null)
            security.checkPermission(shutdownPerm);

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (security != null) { // Check if caller can modify our threads
                for (Worker w : workers)
                    security.checkAccess(w.thread);
            }

            int state = runState;
            if (state < STOP)
                runState = STOP;

            try {
                for (Worker w : workers) {
                    w.interruptNow();
                }
            } catch (SecurityException se) { // Try to back out
                runState = state;
                // tryTerminate() here would be a no-op
                throw se;
            }

            List<Runnable> tasks = drainQueue();
            tryTerminate(); // Terminate now if pool and queue empty
            return tasks;
        } finally {
            mainLock.unlock();
        }
    }

 

 

   3.在jdk5之后,线程的stop()方法已经被不推荐使用,并且没有替代品。文档上如是说:

建议线程通过里面设置boolean runflag,不停轮询该变量来确定是否继续执行(具体做法可以参考下面我的demo)

 

Deprecated. This method is inherently unsafe. Stopping a thread with Thread.stop causes it to 
unlock all of the monitors that it has locked 。 Many uses of stop should be replaced by code that
 simply modifies some variable to indicate that the target thread should stop running. 
The target thread should check this variable regularly, and return from its run method in an orderly
 fashion if the variable indicates that it is to stop running. If the target thread waits for long periods
 (on a condition variable, for example), the interrupt method should be used to interrupt the wait. 

 

 

 

 4.恰不碰巧,我放进去的thread本身就会有很多sleep()操作,

 

sleep会在本线程被interrupt的时候抛异常,

 

    /**	
     * Causes the currently executing thread to sleep (temporarily cease 
     * execution) for the specified number of milliseconds, subject to 
     * the precision and accuracy of system timers and schedulers. The thread 
     * does not lose ownership of any monitors.
     *
     * @param      millis   the length of time to sleep in milliseconds.
     * @exception  InterruptedException if any thread has interrupted
     *             the current thread.  The <i>interrupted status</i> of the
     *             current thread is cleared when this exception is thrown.
     * @see        Object#notify()
     */
    public static native void sleep(long millis) throws InterruptedException;

 

 

interrupte方法如是说明:

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

 

 

 

那么使用ExecutorService来关闭我下面这些线程,碰到可能都会是抛出InterruptedException 。走到异常流程并非

优雅的方式,也就失去了步骤1中的意义。

 

			while(runflag)	{
					//do your business
					try {
						Thread.sleep(30);
					} catch (InterruptedException e) {
						logger.error("msg收取线程sleep异常", e);
					}

				} else if (stRet.iRet == -1) { // 没收到消息!!
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						logger.error("msg收取线程sleep异常", e);
					}
				} else {
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						logger.error("msg收取线程sleep异常", e);
					}
			}			

 

end:

于是乎,只好回归原始.自己通过改变runflag来友好的关闭线程。

	public static void startReceiver() {
		
		for(int i=0;i<rec_thread_count;i++) {
			MsgQReceiver rec = new MsgQReceiver();
			group.add(rec);
			rec.start();
		}
//		ExecutorService pool = Executors.newFixedThreadPool(rec_thread_count);
//		pool.execute(new MsgQReceiver());
	}
 

	/**
	 * 终止消息接收线程组
	 */
	public static void stopReceiver() {
		
		if(group.size()>0) {
			for(MsgQReceiver rec : group) {
				rec.runflag = false;
			}
		}
		
		group = null;
       }
 
   发表时间:2010-03-31  
加一个isInterrupted判断就完了,既然thread被interrupted掉了,那么就不应该用sleep啦。

runflag的变量也应该跟着变化。
0 请登录后投票
   发表时间:2010-04-01  
很多人都对InterruptedException 有误解.
更准确的说是对如何停止一个线程有误解.
企图通过调用一个类似stop语义的方法停止或者杀死一个线程的想法从设计上就是错误的.线程的合理中止本质上就是个很复杂的事情,想通过外部代码直接杀死一个线程,直接的结果就是导致资源的泄漏,事务的不完整或者业务的缺失,如果从语言级别支持这种特性,带来的一定是无穷无尽的后患.你把一系列动作交给一个线程,但是又不想去规划它,而是想通过一个stop随性的想停就停本身就是个不负责的想法.

1 请登录后投票
   发表时间:2010-04-01  
无论用什么,你的thread里本来就要捕捉interrupt异常,这没有什么大惊小怪的。要用好Thread,先研究好interrupt。
0 请登录后投票
   发表时间:2010-04-01  
我觉得是楼主对java线程的interrupt 以及 ExecutorService 中task 的cancel机制理解的不是很正确.强烈推荐看一下Java Concurrency.in.Practice 这本书的第7章.
0 请登录后投票
   发表时间:2010-04-01  
无论如何,上面那个ExecutorService 关闭我的线程确实会抛出interrupt,捕获之后Executor是无法正常关闭的。

后来我改为控制runflag,而不是依赖ExecutorService
0 请登录后投票
   发表时间:2010-04-02   最后修改:2010-04-02
你的方法不完全对,如果是生产消费模型,最佳的终止线程的办法是生成端放入一个所谓的毒药元素,当消费者读到毒药元素,自动从线程中退出,这是最优雅的线程中断模型。
executerService.shutdown会调用中断让线程退出,因此在处理任务的时候一定要恰当处理interrupt,否则线程也退不出来。
0 请登录后投票
   发表时间:2010-04-02  
先学学怎么处理中断异常吧。
0 请登录后投票
   发表时间:2010-04-02  
呵呵,敢问高见啊? 仔细解答一下把。
dennis_zane 写道
先学学怎么处理中断异常吧。

0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics