当Scheduler拿不到url的 时候,不能立即退出
2016-01-21 21:36
411 查看
在webmagic的多线程抓取中有一个比较麻烦的问题:当Scheduler拿不到url的 时候,不能立即退出,需要等到没抓完的线程都运行完毕,没有新url产生时,才能退出。之前使用Thread.sleep来实现,当拿不到url 时,sleep一段时间再取,确定没有线程执行之后,再退出。
但是这种方式始终不够优雅。Java里面有wait/notify机制可以解决这种同步的问题。于是webmagic 0.4.0用wait/notify机制代替了之前的Thread.sleep机制。代码如下:
这里当线程完成之后,会调用
0.4.0发布之后,有用户问我,为什么有的时候抓完无法退出?我开始就怀疑这里可能存在线程安全问题,但是苦于无法复现。
思考了一下,有可能存在这样执行情况:
threadAlive>0,执行
此时最后一个子线程执行结束,
此时主线程进入
那么似乎在lock里加入double-check就OK了?但是今天看了http://coolshell.cn/articles/4576.html这篇文章,大概意思是:出了问题不要靠猜!一定要复现并测试!
于是决定手动模拟!开启10个线程,并mock了所有部件,循环10000次去执行,代码不贴了,地址:https://github.com/code4craft/webmagic/blob/master/webmagic-core/src/test/java/us/codecraft/webmagic/SpiderTest.java。执行一下,果然到了第13次就卡住了!jstack之后,果然卡在
然后加入double-check:
结果执行成功!至此问题解决!
经过这个例子,也大致明白了为什么wait/notify之前总是要先
很简单,是吧?其实这篇文章只想说明一件事:出了bug不要靠猜!一定要复现并确认解决!
但是这种方式始终不够优雅。Java里面有wait/notify机制可以解决这种同步的问题。于是webmagic 0.4.0用wait/notify机制代替了之前的Thread.sleep机制。代码如下:
while (!Thread.currentThread().isInterrupted() && stat.get() == STAT_RUNNING) { Request request = scheduler.poll(this); if (request == null) { if (threadAlive.get() == 0 && exitWhenComplete) { break; } // wait until new url added waitNewUrl(); } else { final Request requestFinal = request; threadAlive.incrementAndGet(); executorService.execute(new Runnable() { @Override public void run() { try { processRequest(requestFinal); } catch (Exception e) { logger.error("download " + requestFinal + " error", e); } finally { threadAlive.decrementAndGet(); signalNewUrl(); } } }); } } private void waitNewUrl() { try { newUrlLock.lock(); try { newUrlCondition.await(); } catch (InterruptedException e) { } } finally { newUrlLock.unlock(); } }
这里当线程完成之后,会调用
signalNewUrl()来通知主线程,停止等待!
0.4.0发布之后,有用户问我,为什么有的时候抓完无法退出?我开始就怀疑这里可能存在线程安全问题,但是苦于无法复现。
思考了一下,有可能存在这样执行情况:
threadAlive>0,执行
if (threadAlive.get() == 0 && exitWhenComplete)check跳过,于是准备进入
waitNewUrl();
此时最后一个子线程执行结束,
threadAlive.decrementAndGet();和
signalNewUrl();相继执行;
此时主线程进入
waitNewUrl(),结果已无线程执行,也无人可以notify它了,于是线程一直等待…
那么似乎在lock里加入double-check就OK了?但是今天看了http://coolshell.cn/articles/4576.html这篇文章,大概意思是:出了问题不要靠猜!一定要复现并测试!
于是决定手动模拟!开启10个线程,并mock了所有部件,循环10000次去执行,代码不贴了,地址:https://github.com/code4craft/webmagic/blob/master/webmagic-core/src/test/java/us/codecraft/webmagic/SpiderTest.java。执行一下,果然到了第13次就卡住了!jstack之后,果然卡在
newUrlCondition.await();这里!
然后加入double-check:
private void waitNewUrl() { try { newUrlLock.lock(); //double check if (threadAlive.get() == 0 && exitWhenComplete) { return; } try { newUrlCondition.await(); } catch (InterruptedException e) { } } finally { newUrlLock.unlock(); } }
结果执行成功!至此问题解决!
经过这个例子,也大致明白了为什么wait/notify之前总是要先
lock。为什么呢?有机会写一篇文章总结一下吧!
很简单,是吧?其实这篇文章只想说明一件事:出了bug不要靠猜!一定要复现并确认解决!
相关文章推荐
- Activity深入理解(一)
- 当Scheduler拿不到url的 时候,不能立即退出
- ubuntu启动各项功能配置+开机自动启动
- poj 3273 Monthly Expense (二分)
- 【Hdu 4117】GRE Words
- x265-1.7版本-common/bitstream.h注释
- centos7 64位 伪分布安装cdh5.5.1,及其测试笔记
- To the Virgins, to Make Much of Time
- PagedDataSource数据绑定控件和AspNetPager分页控件结合使用列表分页
- Nginx负载均衡集群
- 机器学习课程知识总结
- php字符串处理
- 【HDU2222】Keywords Search
- TextView 字体渐变
- C语言判断当前机器大端小端
- MVC设计框架
- 墨菲定律
- 记一次Trouble Shooting的过程: LNK1112: module machine type 'x64' conflicts with target module type 'X86'
- CSS 布局实例系列(一)总结CSS居中的多种方法
- ZOJ 2110 Tempter of the Bone(条件迷宫DFS,HDU1010)