首先研究下Collection下的同步和非同步,例如ArrayList
List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现
List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector
类,除了此类是不同步的。)size、isEmpty、get、set、iterator 和
listIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n
个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向
ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList
实例的容量。这可以减少递增式再分配的数量。
注意,此实现不是同步的。
如果多个线程同时访问一个 ArrayList
实例,而其中至少一个线程从结构上修改了列表,那么它必须
保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用
Collections.synchronizedList
方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(new ArrayList(...));
此类的 iterator 和 listIterator
方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add
方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 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();
}
}
}
}
}
}
分享到:
相关推荐
3. **集合框架**:深入理解ArrayList、LinkedList、HashSet、HashMap等数据结构的工作原理,以及它们之间的区别和选择依据。还要熟悉Collection和Iterable接口,以及Stream API的使用。 4. **多线程**:了解线程的...
首先,我们关注Java集合框架的基础——Collection接口。Collection是所有集合类型的顶级接口,它定义了一组通用的操作方法,如add()、remove()和iterator()。Collection接口的主要特点是它存储一组不重复的元素,...
1. **集合接口**:Java集合框架主要包括两种基本接口——`List`和`Set`。`List`接口存储有序的元素,允许重复;`Set`接口存储不重复的元素。此外,还有`Queue`、`Deque`等其他接口。 2. **实现类**:Java提供了多种...
10. **Java集合框架**:Java集合框架包括List、Set、Queue和Map接口,以及ArrayList、LinkedList、HashSet、HashMap等实现类。框架提供了丰富的操作方法,便于对数据进行增删改查。 通过学习以上知识点,初学者可以...
Java集合框架与泛型 - **Collection与Set接口**:解释Collection接口及其子接口Set的不同之处。 - **List接口**:介绍List接口的特点及其实现类。 - **Map接口**:解释Map接口及其使用方法。 - **Collections类**:...
- **Collection接口**:集合框架的基础接口。 - **Set接口**:不允许包含重复元素的集合。 - **List接口**:允许包含重复元素的有序集合。 - **Map接口**:存储键值对映射关系的集合。 - **泛型**:用于指定集合中...
【Java SE核心2】是达内教育TTS5.0课程体系中的一...通过学习《达内JAVA TTS5.0 PDF——JAVA SE核心2》中的内容,开发者可以提升编程技巧,更好地应对复杂项目的需求,为后续的Java EE和Java框架学习打下坚实的基础。
- **补充标准Java集合框架**:提供了一系列额外的功能和工具,弥补了Java标准集合框架中的不足。 - **计划迁移至JCF**:文档提到Google Collections的部分内容最终可能会被整合进Java标准集合框架中。 - **广泛...
在Java 2之前,也就是Java 1时代,集合框架并不完善,仅有一些基础的容器类,如Vector、Stack和Hashtable,这些类虽然简单易用,但在功能上相对有限,比如缺乏对容器排序的支持。 随着Java 2的发布,集合框架得到了...
Java集合框架是Java编程语言中处理数据结构的一个强大工具包,它提供了一系列灵活高效的接口和实现来帮助开发者管理数据。本篇文章将重点介绍Java中常用的集合类——`Collection`和`Map`及其相关子类,并探讨它们...
- `Collection`接口是所有集合框架的根接口,包括`Set`和`List`等。 - `Collections`工具类提供了一系列用于操作集合的方法,如排序、搜索等。 #### 10. 逻辑运算符 && 与 & 的区别 - `&&`和`&`都可以作为逻辑与...
### Java基础面试宝典——集合篇 ...通过以上内容的学习,你可以更好地理解Java集合框架的基本概念、特点以及如何在实际开发中合理选择和使用各种集合类型。希望这些知识能够帮助你在接下来的面试中取得好成绩!
4. **集合框架**: - **List, Set, Map接口**:ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap等实现。 - **泛型**:限制集合元素类型,增强类型安全。 - **迭代器(Iterator)**:遍历集合对象。 ...
3. **内存管理**:了解Java的内存模型,特别是栈内存和堆内存的区别,以及自动内存管理机制——垃圾回收(Garbage Collection)。 4. **异常处理**:学习如何使用try-catch-finally语句进行异常处理,以及不同类型...
- **集合框架**:熟练使用Collection接口及其子接口List、Set、Queue,以及Map接口。研究各种实现类的内部结构和操作原理,如ArrayList、LinkedList、HashSet、HashMap等。 - **面向对象与面向接口**:理解封装、...
Java集合框架主要由两个核心接口——`Collection`和`Map`构建。`Collection`接口又派生出三个子接口:`Set`、`List`和`Queue`。而`Map`接口则是独立的一类,用于存储键值对。 `Set`接口代表无序且不允许元素重复的...
Java集合框架是一组接口和类,用于存储和操作对象。主要接口有List(有序,允许重复元素)、Set(不允许重复元素)和Map(键值对)。ArrayList和LinkedList实现了List接口,HashSet和TreeSet实现了Set接口,HashMap...
《手机应用——Memory Collection》 Memory Collection是一款精心打造的手机应用程序,由开发者花费近一个月的时间完成,它充分利用了外部库资源,集成了丰富的功能和高效性能。本文将深入探讨这款应用背后的技术...