声明:文章内容全都是自己的学习总结,如有不对的地方请大家帮忙指出。有需要沟通交流的可加我QQ群:425120333
在项目开发中,很容易碰到多个任务同时进行,而在执行这些任务时,因为各个原因导致执行速度而不一致。举个例子,我曾经参与过的爬虫工程,
假设要爬取一个网页,我是根据分类来爬取的,因为各个分类下的页数是不一致的,如果将这些分类平均的分配给一定量的线程,就会使得有些线程很快就结束了,
而有些线程则需要较长的时间,如果最后还要对这些所有获取到的分类数据做处理,则由于有些分类速度慢,从而使得整体的速度变慢了。
如果使用了Fork/Join框架这就不一样了,他采用的是分治法,Fork就是将一个大的任务切分成多个子任务,每个子任务分别去执行,
而Join就是获取各个子任务的执行结果,然后合并。在这过程中没有平均分配为几个任务的概念,一个子任务执行好,就去执行下一个,
这样就避免了因为某一个任务执行速度慢,从而导致的整体速度变慢的问题。
代码示例:
public class TestForkJoin {
public static void main(String[] args) {
//模拟分类列表
List<String> categroyList = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
categroyList.add("分类 _" + i);
}
//执行爬虫任务
CrawlerDemo demo = new CrawlerDemo(categroyList);
ForkJoinPool pool = new ForkJoinPool();
Map<String, Integer> resultMap = pool.invoke(demo);
//打印结果
for (Entry<String, Integer> entry : resultMap.entrySet()) {
System.out.println(entry.getKey() + " 获取到的数字是 " + entry.getValue());
}
}
}
/**
* @author pc_cqb
* 模拟爬虫 (随机生成一个数返回,并sleep相应的时间)
*/
class CrawlerDemo extends RecursiveTask<Map<String, Integer>> {
Random random = new Random();
List<String> categroyList;
public CrawlerDemo(List<String> categroyList) {
this.categroyList = categroyList;
}
@Override
protected Map<String, Integer> compute() {
Map<String, Integer> resultMap = new HashMap<String, Integer>();
if (categroyList.size() == 1) {
//按每一个分类去执行
resultMap.putAll(crawler(categroyList.get(0)));
} else {
int middle = categroyList.size() / 2;
CrawlerDemo demo1 = new CrawlerDemo(categroyList.subList(0, middle));
CrawlerDemo demo2 = new CrawlerDemo(categroyList.subList(middle, categroyList.size()));
demo1.fork();
demo2.fork();
resultMap.putAll(demo1.join());
resultMap.putAll(demo2.join());
}
return resultMap;
}
private Map<String, Integer> crawler(String categeryDesc) {
Map<String, Integer> resultMap = new HashMap<String, Integer>();
Integer result = random.nextInt(500);
try {
TimeUnit.MILLISECONDS.sleep(result);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
resultMap.put(categeryDesc, result);
return resultMap;
}
}
控制台显示
分类 _6 获取到的数字是 45
分类 _7 获取到的数字是 412
分类 _4 获取到的数字是 326
分类 _5 获取到的数字是 266
分类 _8 获取到的数字是 433
分类 _9 获取到的数字是 482
分类 _2 获取到的数字是 374
分类 _3 获取到的数字是 446
分类 _0 获取到的数字是 339
分类 _1 获取到的数字是 56
这里继承的是RecursiveTask这个抽象类,因为是需要有返回值的,如果不需要返回值的可以继承RecursiveAction这个抽象类,
根据自己的需求而选择。这里是通过调用ForkJoinPool来获取最后的结果,使用的是invoke方法直接获取到的结果,
也可以通过submit方法获取到一个Future对象,在通过get()得到结果(这里不贴代码了);
到这里我自己觉得实现多线程的方法就说的差不多了,当然还有其他的,比如CountDownLatch(与CycleBarrier有些类似)等,就不在逐个描述了,
写这些只是对自己学习过程中的归纳,自己学到了,自己给表述出来(水平有限,想到什么写什么),希望也能帮助一些别人。
接下来要说的就是关于多线程之间共享变量以及线程之间通信相关的内容了,让我们共同学习,一起进步!!!