Java 集合与队列的插入、删除在并发下的性能比较
这两天在写一个java多线程的爬虫,以广度优先爬取网页,设置两个缓存:
- 一个保存已经访问过的URL:vistedUrls
- 一个保存没有访问过的URL:unVistedUrls
需要爬取的数据量不大,对URL压缩后,可以把这两个数据结构都放入内存,vistedUrls很显然用HashSet<String>实现,因为已经访问的URL只会添加,不会删除和修改,使用HashSet可以高效判断一个URL是否已经访问。
纠结unVistedUrls该用什么数据结构,如果用队列的话,并发情况下,队列中可能会用重复的URL,比如一个线程A爬了CSDN的一个URL1,另一个线程B爬了博客园的一个URL2,URL1和URL2的页面都有一个相同的出链URL3,线程A把URL3加入到unVistedUrls的队尾,等待下次爬取,但在URL3被爬取之前,线程B也把URL3加到队尾,这样队列中就有两个相同的URL,可能会导致重复爬取网页,当然可以通过其他方法来保证不会重复爬取。
然后就想能否也用Set来保存未访问的URL,这样在添加新的URL时,自动去重处理了,能够有效保证不爬取重复网页。但是unVistedUrls会有大量的插入和删除操作,我认为对集合进行大量的插入删除性能会比较低,为了测试集合的插入删除性能对比队列低多少,我写了一个简单的并发测试:
- /**
- * 测试集合与队列的插入与读写性能
- *
- * @author jiqunpeng@gmail.com
- *
- */
- public class SetQueueTest {
- // 随即数构造器
- private static Random r = new Random(10);
- // 控制测试线程停止的原子变量
- private static AtomicBoolean stop = new AtomicBoolean(false);
- /***
- * 基类,供测试用
- *
- * @author jiqunpeng@gmail.com
- *
- */
- static abstract class Service {
- // 操作的计数器
- protected long count = 0;
- // 添加一堆元素,并去一个元素
- public abstract String addAndPick(List<String> elements);
- // 取一个元素
- public abstract String pickOne();
- /**
- * 打印操作次数
- */
- public void tell() {
- System.out.println(this + " :\t" + count);
- }
- }
- /***
- * 采用TreeSet的集合工具
- *
- * @author jiqunpeng@gmail.com
- *
- */
- static class SetService extends Service {
- private TreeSet<String> set = new TreeSet<String>();
- @Override
- public synchronized String addAndPick(List<String> elements) {
- count++;
- set.addAll(elements);
- return set.pollFirst();
- }
- @Override
- public synchronized String pickOne() {
- count++;
- return set.pollFirst();
- }
- }
- /***
- * 采用LinkedList的队列工具
- *
- * @author jiqunpeng@gmail.com
- *
- */
- static class QueueService extends Service {
- private Queue<String> queue = new LinkedList<String>();
- @Override
- public synchronized String addAndPick(List<String> elements) {
- count++;
- queue.addAll(elements);
- return queue.poll();
- }
- @Override
- public synchronized String pickOne() {
- count++;
- return queue.poll();
- }
- }
- /***
- * 测试类
- *
- * @author jiqunpeng@gmail.com
- *
- */
- static class Tester implements Runnable {
- // 绑定要测试的工具对象
- private Service service;
- Tester(Service s) {
- this.service = s;
- }
- @Override
- public void run() {
- while (stop.get() == false) {
- List<String> elements = new ArrayList<String>();
- int len = r.nextInt(200) + 8;
- for (int i = 0; i < len; i++) {
- elements.add(String.valueOf(r.nextInt()));
- }
- service.addAndPick(elements);
- for (int i = 0; i < 104; i++)
- service.pickOne();
- }
- }
- }
- /***
- * 多线程方式,测试一个插入、删除工具
- *
- * @param service
- * @param time
- * @param unit
- * @throws InterruptedException
- */
- private static void test(Service service, int time, TimeUnit unit)
- throws InterruptedException {
- ExecutorService execs = Executors.newCachedThreadPool();
- for (int i = 0; i < 20; i++) {
- execs.execute(new Tester(service));
- }
- execs.shutdown();
- unit.sleep(time);
- stop.compareAndSet(false, true);
- service.tell();
- }
- public static void main(String[] args) throws InterruptedException {
- Service setService = new SetService();
- test(setService, 5, TimeUnit.SECONDS);
- stop.compareAndSet(true, false);// 重置终止条件
- Service queueService = new QueueService();
- test(queueService, 5, TimeUnit.SECONDS);
- }
- }
输出的结果如下:
- SetQueueTest$SetService@5e9de959 : 7149859
- SetQueueTest$QueueService@11b343e0 : 24303408
测试结果让我感到吃惊,TreeSet的插入删除效率确实比LinkedList低,20个线程跑了10秒,使用队列,插入删除24303408次,使用集合,插入删除7149859次。它们之间差距并不大,队列只比集合快2~3倍。属于同一个数量级。于是我这个小型的爬虫应该放心的选择用Set作为unVistedUrls的实现。
转载请注明出处:www.cnblogs.com/fengfenggirl
Java 集合与队列的插入、删除在并发下的性能比较的更多相关文章
- Java集合--阻塞队列及各种实现的解析
阻塞队列(Blocking Queue) 一.队列的定义 说的阻塞队列,就先了解下什么是队列,队列也是一种特殊的线性表结构,在线性表的基础上加了一条限制:那就是一端入队列,一端出队列,且需要遵循FIF ...
- Java 集合深入理解(10):Deque 双端队列
点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ 什么是 Deque Deque 是 Double ended queue (双端队列) 的缩写,读音和 deck 一样,蛋 ...
- Java 集合深入理解(9):Queue 队列
点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ 今天心情不太好,来学一下 List 吧! 什么是队列 队列是数据结构中比较重要的一种类型,它支持 FIFO,尾部添加.头部 ...
- Java多线程 阻塞队列和并发集合
转载:大关的博客 Java多线程 阻塞队列和并发集合 本章主要探讨在多线程程序中与集合相关的内容.在多线程程序中,如果使用普通集合往往会造成数据错误,甚至造成程序崩溃.Java为多线程专门提供了特有的 ...
- java集合详解(附栈,队列)
1 集合 1.1 为什么会出现集合框架 [1] 之前的数组作为容器时,不能自动拓容 [2] 数值在进行添加和删除操作时,需要开发者自己实现添加和删除. 1.2 Collection接口 1.2.1 C ...
- Java集合总结(一):列表和队列
java中的具体容器类都不是从头构建的,他们都继承了一些抽象容器类.这些抽象容器类,提供了容器接口的部分实现,方便具体容器类在抽象类的基础上做具体实现.容器类和接口的关系架构图如下: 虚线框表示接口, ...
- 对JAVA集合进行遍历删除时务必要用迭代器
java集合遍历删除的方法: 1.当然这种情况也是容易解决,实现方式就是讲遍历与移除操作分离,即在遍历的过程中,将需要移除的数据存放在另外一个集合当中,遍历结束之后,统一移除. 2.使用Iterato ...
- java集合循环删除
java集合循环删除,java list集合操作,java循环.分享牛,分享牛原创.java集合删除方法. 2.6.1.第一种方式 list.add("1"); list.add( ...
- java集合遍历删除指定元素异常分析总结
在使用集合的过程中,我们经常会有遍历集合元素,删除指定的元素的需求,而对于这种需求我们往往使用会犯些小错误,导致程序抛异常或者与预期结果不对,本人很早之前就遇到过这个坑,当时没注意总结,结果前段时间又 ...
随机推荐
- 数据库 SQL语句小结(更新中)
################ Navicat,单条执行sql ################ Navicat,数据库管理工具, 在查询的页面有好多命令,若单条执行: 1:可选中要执行的一条sql ...
- PMBOK(第五版)学习笔记二-十大知识领域(P87)
五大项目管理过程组:启动.规划.执行.监控.收尾过程组 十大知识领域是:项目整合管理.项目范围管理.项目时间管理.项目成本管理.项目质量管理.项目人力资源管理.项目沟通管理.项目风险管理.项目采购管理 ...
- MySQL online ddl原理
背景 dba的日常工作肯定有一项是ddl变更,ddl变更会锁表,这个可以说是dba心中永远的痛,特别是执行ddl变更,导致库上大量线程处于“Waiting for meta data lock”状态的 ...
- SQL SERVER 查看数据库表的字段类型,是否允许为NULL,默认值,主键等
)-- 表名 set @table_name='bqcform101' --============表结构 select 类别,表名or字段名,描述,字段类型,是否自增,允许为NULL,默认值 fro ...
- Makefile隐含规则
两个隐含规则; 将所有的name.o的依赖自动推导为name.c并使用规则$(CC) -c $(FLAGS) $(CPPFLAGS)得到目标.这个规则中只有-c是隐含规则中有的,后面两个变量是留给用户 ...
- CCommandManager 类
CCommandManager类 CCommandManager 类管理命令及其与图像的关联.描述CCommandManager :: CleanUp从命令列表和全局和用户映射中删除所有元素.CCom ...
- ab.exe使用
ab.exe是一个性能检测工具,是apache server中的一个小组件,使用简单,方便 下载地址:http://files.cnblogs.com/files/gossip/ab.zi ...
- JavaScript日期控件,用select实现
<!doctype html> <html> <head> <title>年月日</title> </head> <bod ...
- hdu Dylans loves tree [LCA] (树链剖分)
Dylans loves tree view code#pragma comment(linker, "/STACK:1024000000,1024000000") #includ ...
- C#基础---事件的使用
一:什么是事件 事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框.每一种控件有自己可以识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件,等等.事 ...