Java 集合与队列的插入、删除在并发下的性能比较
2014-05-18 00:18
489 查看
这两天在写一个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会有大量的插入和删除操作,我认为对集合进行大量的插入删除性能会比较低,为了测试集合的插入删除性能对比队列低多少,我写了一个简单的并发测试:
输出的结果如下:
测试结果让我感到吃惊,TreeSet的插入删除效率确实比LinkedList低,20个线程跑了10秒,使用队列,插入删除24303408次,使用集合,插入删除7149859次。它们之间差距并不大,队列只比集合快2~3倍。属于同一个数量级。于是我这个小型的爬虫应该放心的选择用Set作为unVistedUrls的实现。
转载请注明出处:www.cnblogs.com/fengfenggirl
一个保存已经访问过的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 集合与队列的插入、删除在并发下的性能比较
- Java 集合与队列的插入、删除在并发下的性能比较
- 【Java 集合与队列的插入、删除在并发下的性能比较】
- 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue在入队操作高并发性能比较
- Java多线程 阻塞队列和并发集合
- Java中不同的并发实现的性能比较
- 常见数据结构查找、插入、删除、遍历性能比较 常见排序算法的比较(图)
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- java-集合类(3)-实现比较器(Comparator)接口-LinkedList针对插入删除、开始处增加元素
- Java多线程 阻塞队列和并发集合
- Java中不同的并发实现的性能比较
- 常见数据结构查找、插入、删除、遍历性能比较 常见排序算法的比较(图)
- Java队列集合的性能测试
- JAVA的synchronized关键字与concurrent并发包的性能比较。
- Java 中常用集合性能比较
- Java多线程 阻塞队列和并发集合
- Java多线程 阻塞队列和并发集合
- Java中不同的并发实现的性能比较