`

Collections.synchronizedList【转】

 
阅读更多

首先研究下Collection下的同步和非同步,例如ArrayList

 

    List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)sizeisEmptygetsetiteratorlistIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。

 

       注意,此实现不是同步的。

 

       如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:

 

        List list = Collections.synchronizedList(new ArrayList(...)); 

 

此类的 iteratorlistIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 removeadd 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

 

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。

 

      如上所示,现在建立一个list集合,一个线程对集合进行写入操作,一个线程进行删除操作

 

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

public class MyArrayList {
	/**
	 * 创建一个列表,一个线程进行写入,一个线程读取 iterator 和 listIterator 方法返回的迭代器是快速失败的
	 */
	public void readWrite() {
		List<Integer> nums = new ArrayList<Integer>();
		
		List<Integer> synNums = Collections.synchronizedList(nums);
		
		//启动写入线程
		new WriteListThread(synNums).start();
		
		//启动删除线程
		new DeleteListThread(synNums).start();
	}

	public static void main(String[] args) {
		new MyArrayList().readWrite();
	}
}

class WriteListThread extends Thread {
	private List<Integer> nums;

	public WriteListThread(List<Integer> nums) {
		super("WriteListThread");
		this.nums = nums;
	}

	// 不停写入元素1
	public void run() {
		while (true) {
                    nums.add(new Random().nextInt(1000));	                                                                                                                                                System.out.println(Thread.currentThread().getName());
		
		}
	}
}

class DeleteListThread extends Thread {
	private List<Integer> nums;

	public DeleteListThread(List<Integer> nums) {
		super("DeleteListThread");
		this.nums = nums;
	}

	// 删除第一个元素
	public void run() {
		while (true) {
			try{
				System.out.println(Thread.currentThread().getName()+":"+nums.remove(0));
			}catch(Exception e){
				continue ;
			}
		}
	}
}

 通过List<Integer> synNums = Collections.synchronizedList(nums);就能对原子操作进行同步了,但是官方api示例为什么要自己手动添加同步呢?

 List list = Collections.synchronizedList(new ArrayList());
  synchronized(list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

  查看Collections.synchronizedList的源代码

SynchronizedCollection(Collection<E> c) {
            if (c==null)
                throw new NullPointerException();
	    this.c = c;
            mutex = this;
}

 

public boolean add(E e) {
synchronized(mutex) {return c.add(e);}
        }
public boolean remove(Object o) {
	    synchronized(mutex) {return c.remove(o);}
        }

//没有进行同步操作
public Iterator<E> iterator() {
            return c.iterator(); // 由用户自己手动同步
        }

 可见对于集合同步操作,使用Collections的同步包装工具类,还需要对非原子操作用户还需要手动进行同步

如下所示,加一个线程,对集合进行读取

class ReadListThread extends Thread {
	private List<Integer> nums;

	public ReadListThread(List<Integer> nums) {
		super("ReadListThread");
		this.nums = nums;
	}

	// 不停读取元素,非原子操作,则需要手动加上锁
	public void run() {
		while (true) {
                        //休眠,将锁交给其他线程
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			synchronized (nums) {
				if (nums.size() > 100) {
					Iterator<Integer> iter = nums.iterator();
					while (iter.hasNext()) {
						System.out.println(Thread.currentThread().getName()
								+ ":" + iter.next());
						;
					}
				}else{
					try {
						nums.wait(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

 

 

 

 

 

分享到:
评论

相关推荐

    Collections.synchronizedList

    NULL 博文链接:https://username2.iteye.com/blog/1812796

    spring-data-mongodb-test:在Collections.synchronizedList或Collections.synchronizedSet上测试spring数据mongodb ConcurrentModificationException

    Spring数据mongodb测试 在Collections.synchronizedList或Collections.synchronizedSet上测试spring数据mongodb ConcurrentModificationException

    Java的线程安全与不安全集合.docx

    使用Collections.synchronizedList(new ArrayList()):内部直接将接受的List对象传递给静态内部类SynchronizedList对象,然后Collections.synchronizedList(new ArrayList())返回的List对象的调用方法都是直接调用...

    java中SynchronizedList和Vector的区别详解

    主要介绍了java中SynchronizedList和Vector的区别详解,Vector是java.util包中的一个类。 SynchronizedList是java.util.Collections中的一个静态内部类。,需要的朋友可以参考下

    javabiginteger源码-MultiThreadMode:多线程模式

    java.util.ArrayList非线程安全的类,可用Collections.synchronizedList()进行包装 List list = Collections.synchronizedList(new ArrayList()); CopyOnWriteArrayList线程安全的类,适用于读操作频繁的场景。 Gua

    java ArrayList和Vector的区别详解

    主要介绍了java ArrayList和Vector的区别详解的相关资料,并附简单实例代码,需要的朋友可以参考下

    JDKAPI18CN(中文版)

    如果没有这样的对象存在,列表应该使用Collections.synchronizedList方法“包装”。 这最好在创建时完成,以防止意外的不同步访问列表: List list = Collections.synchronizedList(new ArrayList(...)); The ...

    ArrayList.java

    如果该对象不存在,该列表应被“包装”使用Collections.synchronizedList方法。 这最好在创建时完成,以防止意外的名单不同步访问: List list = Collections.synchronizedList(new ArrayList(...)); 此类的返回的...

    Java的CopyOnWriteArrayList功能详解,及无法保证数据是实时同步.docx

    高并发的情况下,一般都要求性能要给力,Vector 显然不够格,所以被遗忘在角落也是“罪有应得”啊。...那有些同学可能会说,可以使用 Collections.synchronizedList() 让 ArrayList 变成线程安全啊。

    java关键字ArrayList详解

    另一方面,由于ArrayList是线程不安全的,因此在使用ArrayList时需要注意多线程同步问题,可以通过使用线程安全的Collections.synchronizedList()方法来创建线程安全的ArrayList。 总之,ArrayList是Java中一种很...

    Java 高频面试题:聊一聊 JUC 下的 CopyOnWriteArrayList

    ArrayList 是我们常用的工具类之一,但是在多线程的情况下,ArrayList 作为共享变量时,并不是线程安全的。...用 Collections.SynchronizedList ; 用 JUC 下的 CopyOnWriteArrayList ; 先来看看 Synchron

    Java容器.xmind

    synchronizedList synchronizedSet synchronizedMap synchronizedSortedSet synchronizedSortedSet JUC CopyOnWriteArrayList 写有锁,读无锁,读写之间不阻塞,优于读写锁 写入时先copy一个容器副本,再添加...

Global site tag (gtag.js) - Google Analytics