java5的java.util包提供了大量集合类。其中最常用的集合类有List、Set、Map等。这篇文章主要介绍其中的Map。
首先,来看下java.util包中Map相关的集合类的类图(见附件中图1)。接口Map是整个类图的跟,Map往下又提供了两个接 口:ConcurrentMap和SortedMap。ConcurrentMap是java5中新增的线程安全的Map接口;而SortedMap则是 支持排序的Map接口。在下面这些具体的实现类中,常用的就属Hashtable、HashMap和TreeMap了。另外,java5新增了 HashMap的并发版本ConcurrentHashMap。下面主要介绍下这几个类。
在开始之前,先介绍下Map是什么?
javadoc中对Map的解释如下:
This interface takes the place of the Dictionary class, which was a totally abstract class rather than an interface.
The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings.
从上可知,Map用于存储“key-value”元素对,它将一个key映射到一个而且只能是唯一的一个value。
Map可以使用多种实现方式,HashMap的实现采用的是hash表;而TreeMap采用的是红黑树。
1. Hashtable 和 HashMap
这两个类主要有以下几方面的不同:
Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类。
在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。 当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null 。
这两个类最大的不同在于Hashtable是线程安全的,它的方法是同步了的,可以直接用在多线程环境中。而HashMap则不是线程安全的。在多线程环 境中,需要手动实现同步机制。因此,在Collections类中提供了一个方法返回一个同步版本的HashMap用于多线程的环境:
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { return new SynchronizedMap<K,V>(m); }
该方法返回的是一个SynchronizedMap 的实例。SynchronizedMap类是定义在Collections中的一个静态内部类。它实现了Map接口,并对其中的每一个方法实现,通过synchronized 关键字进行了同步控制。
2. 潜在的线程安全问题
上面提到Collections为HashMap提供了一个并发版本SynchronizedMap。这个版本中的方法都进行了同步,但是这并不等于这个类就一定是线程安全的。在某些时候会出现一些意想不到的结果。
如下面这段代码:
// shm是SynchronizedMap的一个实例 if(shm.containsKey('key')){ shm.remove(key); }这 段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的containsKey和reomve方法都是同步的,但是整段代码却不是。考虑这么 一个使用场景:线程A执行了containsKey方法返回true,准备执行remove操作;这时另一个线程B开始执行,同样执行了 containsKey方法返回true,并接着执行了remove操作;然后线程A接着执行remove操作时发现此时已经没有这个元素了。要保证这段 代码按我们的意愿工作,一个办法就是对这段代码进行同步控制,但是这么做付出的代价太大。
在进行迭代时这个问题更改明显。Map集合共提供了三种方式来分别返回键、值、键值对的集合:
Set<K> keySet(); Collection<V> values(); Set<Map.Entry<K,V>> entrySet();在这三个方法的基础上,我们一般通过如下方式访问Map的元素:
Iterator keys = map.keySet().iterator(); while(keys.hasNext()){ map.get(keys.next()); }
在这里,有一个地方需要注意的是:得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本” 。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能会抛出ConcurrentModificationException异常。为了解决这个问题通常有两种方法,
一是直接返回元素的副本,而不是视图。这个可以通过
集合类的 toArray() 方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。
3. 更好的选择:ConcurrentHashMap
java5中新增了ConcurrentMap接口和它的一个实现类ConcurrentHashMap。ConcurrentHashMap提供了和 Hashtable以及SynchronizedMap中所不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能 由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个 桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是 显而易见的。
上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。
在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException, 取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。
相关推荐
在Java编程中,多线程安全集合是程序员在并发环境下处理数据共享时必须考虑的关键概念。这些集合确保了在多个线程访问时的数据一致性、完整性和安全性,避免了竞态条件、死锁和其他并发问题。Java提供了一系列的线程...
无论是使用Collections.synchronizedMap()、ConcurrentHashMap还是避免在多线程环境中使用,都需要根据应用的具体场景来权衡性能与安全。在设计和编写多线程程序时,要始终关注数据结构的选择和操作的同步控制,以...
java_各个Map的区别 ConcurrentHashMap 支持检索的完全并发和更新的所期望可调整并发的哈希表。(线程安全)此类遵守与 Hashtable 相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。不过,尽管所有...
5. **线程安全**:在多线程环境中,确保对缓存的操作是线程安全的。如果是并发访问,应使用ConcurrentHashMap。 6. **缓存更新与一致性**:当源数据发生更改时,需要同步更新缓存,以保持数据一致性。 7. **缓存...
Java中的Map接口是Java集合框架的重要组成部分,它用于存储键值对的数据结构。Map不同于List,List是以索引来访问元素,而Map则是通过键(key)来查找对应的值(value)。Map接口定义了一系列方法,使得我们可以对...
java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 竞态条件 i++ 读i ++ 值写回i 可见性 JMM 由于cpu和内存加载速度的差距,在两者之间增加了多级缓存导致,内存并不能直接对cpu可见。 ...
Vector类与ArrayList类相似,但Vector类是同步的,即线程安全的。 LinkedList类 LinkedList类是List接口的实现类,使用链表来存储对象。LinkedList类提供了多种方法来添加、删除、遍历集合中的对象。例如,add()...
在Java编程语言中,`Map`接口是集合框架的重要组成部分,它存储键值对的数据结构。这个接口提供了许多高效的方法来存储、检索和操作数据。本示例将深入探讨Java中常用的`Map`实现,包括`HashMap`和`EnumMap`。 首先...
在实际应用中,选择合适的Map实现取决于需求,如是否需要有序性、是否需要线程安全等。例如,如果需要线程安全,可以使用ConcurrentHashMap,它在多线程环境下提供了高效的并发操作。 总的来说,Java中的Map映射...
### 对Java中Map集合的深入解析 #### 一、Map集合概述 Map是Java集合框架中的一个重要组成部分,它提供了一种存储键值对(key-value pair)数据结构的方式。与List和Set不同,Map并没有直接继承自`Collection`接口,...
- **ConcurrentHashMap**:线程安全的哈希表,比`synchronized` Map更高效。 - **BlockingQueue**:用于线程间的数据传递,提供阻塞操作,常用于线程池的实现。 - **Atomic类**:提供原子操作,如`AtomicInteger`...
在Java的并发编程中,ConcurrentHashMap 是一个非常重要的组件,它提供了线程安全的HashMap实现。本文将深入探讨 ConcurrentHashMap 的内部实现原理,并通过代码示例展示其使用方法和优势。 通过本文,我们深入探讨...
而Hashtable是古老的线程安全实现,但在多线程环境中通常建议使用ConcurrentHashMap,后者提供了更好的并发性能。 在选择Map实现时,应考虑以下几个因素: 1. 是否需要线程安全性:如果在多线程环境中使用,可以...
5. **数据交换**:线程中的数据交换通常是通过`Socket`的`InputStream`和`OutputStream`进行的。可以使用`BufferedReader`和`PrintWriter`来读写数据。 ```java BufferedReader reader = new BufferedReader(new ...
泛型是Java 5引入的一个重要特性,它增强了类型安全性,并减少了强制类型转换的需要。泛型允许我们在定义类、接口和方法时指定参数类型,从而在编译时捕获类型错误。例如,`ArrayList<T>`就是泛型的一个例子,这里的...
在Java中,线程的创建主要有两种方式:一是继承`Thread`类,二是实现`Runnable`接口。由于Java不支持多重继承,因此使用`Runnable`接口更加灵活。此外,`Callable`接口在JDK 1.5后引入,它可以返回一个结果,并且...
Java中的枚举是一种特殊的类,它提供了一种安全的方式来表示有限集合中的值。枚举类型在Java中被引入,主要用于解决常量集合并提供一种强类型的安全性,防止了无意间创建新的实例或修改枚举常量。这篇博客将深入探讨...
在Java编程领域,`concurrentMap`是并发编程中至关重要的一部分,它提供了线程安全的映射操作。本文将深入探讨`concurrentMap`在Java内存模型(JMM,Java Memory Model)中的实现原理,以及如何通过HotCode优化并发...
不过,这样的实现方式并不具备标准Map接口提供的高级功能,例如自动扩容、线程安全等。在实际开发中,我们通常会优先选择Java提供的HashMap、TreeMap等实现,它们提供了更好的性能和更多的功能。但对于学习和理解Map...
Java集合与多线程是Java编程中的两个核心概念,它们在实际开发中有着广泛的应用。本文将深入探讨这两个主题,并结合实例进行详细讲解。 首先,我们来看Java集合。集合是Java提供的一种数据结构,用于存储多个对象。...