线程清理
执行脚本遇到一个情况,脚本无法自动退出完毕,必须手动执行Ctrl-C才能退出,之前遇到过这个问题是因为数据库连接没有被正确关闭导致,这次的原因是一个另起的线程没有正常停止的原因。
此线程A维护一个队列,线程A负责从队列中消费,主线程负责往线程中添加,队列池用的LinkedBlockQueue。线程A主要工作就是,从queue中take()出一个资源,但是处理相关逻辑,然后循环。
所以在脚本执行完毕后,需要给线程一个通知,当队列为空时,自动退出循环结束线程。然后在主线程中等待theadA.join()即可。
然后的问题是,LinkedBlockQueue.take()是无限等待的,所以还需要执行theadA.interrupt()才能够跳出这个无限等待的API。
但是如果执行theadA.interrupt()时,队列还不为空,就会出问题。
所以使用LinkedBlockQueue.poll(long timeout, TimeUnit unit),就不需要执行theadA.interrupt(),也能够判断到队列为空自动退出循环了。
但是有超时时间的poll方法性能上应该要比无超时时间的take()要低,而且正常执行过程中当队列为空时反复超时跳出等待再进去新的等待,浪费CPU。
这是个有点纠结的问题。
以下代码,用stop()来立刻停止线程(无论queue是否还有剩下的未处理),用interrupt()来等待处理完queue中所有后再退出。
这里或者可以在interrupt()中返回queue.size(),根据返回的size若为0可以执行theadA.interrupt(),不为0则不执行,但是又有问题同步队列的size()方法会引起线程同步的问题(size()方法并没有太大的意义)。
running和interrupt变量是会在不同的线程write和read的,所以必须要用volatile修饰,保证一致性
private volatile boolean running = true; public void stop(){ running = false; } private volatile boolean interrupt = false; public void interrupt(){ interrupt = true; } @Override public void run() { while(running){ try { Object obj = null; try { obj = queue.take(); obj = queue.poll(5, TimeUnit.SECONDS); } catch (InterruptedException e) { logger.error("used to break queue.take() infinite waiting"); } catch (Exception e) { logger.error("get obj erorr", e); } if(obj != null){ do(obj); } if(interrupt){ int size = queue.size(); if(size == 0){ logger.info("interrupt after queue size == 0"); break; } else{ logger.info("interrupt but queue size == " + size); } } } catch (Exception e) { logger.error("erorr", e); } } logger.info("stop succ"); } |