我想对数据访问做一个缓冲,选用Map来做缓冲容器,考虑到效率我选择了HashMap
想想循环往里面仍或者更新数据,那么当系统不访问的时候这些内容,我应该实时的清除这些内存内容
根据需要,我写了一个静态Map做内存容器,然后设置一个Spring定时器来定时检查和处理那些数据需要清除
但是定时器处理时遇到异常 java.util.ConcurrentModificationException ,遇到线程安全问题
查了一下HashMap的API介绍:
注意,此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap
方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
由所有此类的“collection 视图方法”所返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException
。因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
怎么解决呢?
找了一下API,在1.5后有这样一个Map对象ConcurrentHashMap
介绍如下:
支持获取的完全并发和更新的所期望可调整并发的哈希表。此类遵守与 Hashtable
相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。不过,尽管所有操作都是线程安全的,但获取操作不 必锁定,并且不 支持以某种防止所有访问的方式锁定整个表。此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关。
获取操作(包括 get)通常不会受阻塞,因此,可能与更新操作交迭(包括 put 和 remove)。获取会影响最近完成的 更新操作的结果。对于一些聚合操作,比如 putAll 和 clear,并发获取可能只影响某些条目的插入和移除。类似地,在创建迭代器/枚举时或自此之后,Iterators 和 Enumerations 返回在某一时间点上影响哈希表状态的元素。它们不会 抛出 ConcurrentModificationException
。不过,迭代器被设计成每次仅由一个线程使用。
这允许通过可选的 concurrencyLevel 构造方法参数(默认值为 16)来引导更新操作之间的并发,该参数用作内部调整大小的一个提示。表是在内部进行分区的,试图允许指示无争用并发更新的数量。因为哈希表中的位置基本上是随意的,所以实际的并发将各不相同。理想情况下,应该选择一个尽可能多地容纳并发修改该表的线程的值。使用一个比所需要的值高很多的值可能会浪费空间和时间,而使用一个显然低很多的值可能导致线程争用。对数量级估计过高或估计过低通常都会带来非常显著的影响。当仅有一个线程将执行修改操作,而其他所有线程都只是执行读取操作时,才认为某个值是合适的。此外,重新调整此类或其他任何种类哈希表的大小都是一个相对较慢的操作,因此,在可能的时候,提供构造方法中期望表大小的估计值是一个好主意。
此类及其视图和迭代器实现了 Map
和 Iterator
接口的所有可选 方法。
此类与 Hashtable
相似,但与 HashMap
不同,它不 允许将 null 用作键或值。
这两个对象的加载因子都是0.75,因此可以考虑进行替换!
缓存类:
/** * @说明 将监控获取的网络值进行内存缓存 * @author cuisuqiang * @version 1.0 * @since */ public class JianKongMapKeep { /** * 使用线程安全的Map对象 */ public static Map<String,JianKongKeep> keepMaps = new ConcurrentHashMap<String,JianKongKeep>(); /** * 获得某值 * @param key * @return */ public static JianKongKeep getKeepListByKey(String key){ JianKongKeep jianKongKeep = new JianKongKeep(); jianKongKeep = keepMaps.get(key); return jianKongKeep; } /** * 新增或更新 * @param key * @param jianKongKeep */ public static void saveOrUpdateJianKongKeep(String key,JianKongKeep jianKongKeep){ keepMaps.put(key, jianKongKeep); } }
定时器执行类:
/** * @说明 定时清理监控的内存内容 * @author cuisuqiang * @version 1.0 * @since */ public class JianKongMapKeepTimer { public void checkJianKongKeep(){ try { Map<String,JianKongKeep> keepMaps = JianKongMapKeep.keepMaps; for(String key : keepMaps.keySet()){ JianKongKeep jianKongKeep = keepMaps.get(key); Date date = jianKongKeep.getLastUpdate(); Date nowDate = new Date(); long c = DataUtil.TimeDiff(nowDate,date); // 清理大于一分钟的内存内容 if(c > 1 * 60){ keepMaps.remove(key); } } } catch (Exception e) { e.printStackTrace(); } } }
经过测试是没有问题!
如果大家有其他解决办法或者更合理更高效的缓冲解决方案,欢迎指点!
请您到ITEYE看我的原创:http://cuisuqiang.iteye.com
或支持我的个人博客,地址:http://www.javacui.com
相关推荐
java.util.ConcurrentModificationException 解决方法 在使用iterator.hasNext()操作迭代器的时候,如果此时迭代的对象发生改变,比如插入了新数据,或者有数据被删除。 则使用会报以下异常: Java.util....
这个异常的产生是由于集合类(如HashMap)的非线程安全特性,当你在一个线程中使用迭代器遍历集合,而另一个线程在同时修改这个集合时,就会触发此异常。下面我们将深入探讨这个问题的原因、示例代码以及解决策略。 ...
在Java编程中,`ConcurrentModificationException`是一个常见的运行时异常,主要出现在多线程环境下对集合类(如List、Set、Map等)进行并发修改时。然而,这个异常不仅限于多线程环境,即使在单线程中,如果在遍历...
但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除元素),Iterator将抛出ConcurrentModificationException异常。 Set接口是Collection接口的子接口...
// 在多线程环境中使用这些集合进行操作... } } ``` 理解并熟练运用这些线程安全集合是构建健壮、高性能的多线程Java应用程序的基础。它们能帮助开发者编写出更安全、更高效的代码,避免因并发问题导致的错误。...
- `java.util.Set`和`java.util.Map`接口的实现:添加了`LinkedHashSet`和`LinkedHashMap`,保持插入顺序。 - `java.util.Iterator`的改进:支持`remove()`操作,避免抛出`ConcurrentModificationException`。 ### ...
在Java编程语言中,集合框架(`java.util`包)提供了多种容器类来存储对象,如`List`、`Set`和`Map`等。为了遍历这些容器中的元素,Java引入了迭代器模式(Iterator Pattern),这是一种常用的设计模式,它提供了一...
`java.util.concurrent.ForkJoinPool`和`java.util.concurrent.RecursiveTask`是其核心类。 7. **非阻塞堆栈跟踪(Non-blocking Stack Traces)** 当线程处于等待状态时,Java 7可以生成不包含阻塞信息的堆栈跟踪...
在多线程环境中,只读集合特别有用,因为它们能防止并发修改异常(`ConcurrentModificationException`)。当多个线程试图同时修改集合时,可能会出现这种问题。只读集合确保了即使在并发环境下,数据也能保持一致。 ...
否则将会引发 java.util.ConcurrentModificationException 异常。 3. Iterator 迭代器采用的是快速失败(fail-fast)机制,一旦在迭代过程中检测到该集合已经被修改(通常是程序中其它线程修改),程序立即引发 ...
在Java编程中,Map接口是集合框架的一部分,它存储键值对的数据结构。遍历Map是常见的操作,以便访问或处理其中的元素。本篇文章将详细介绍如何在Java中实现遍历Map,特别是针对HashMap的遍历技巧。 首先,Java提供...
1. **使用线程安全的类**:Java提供了一些线程安全的Map实现,如`java.util.concurrent.ConcurrentHashMap`。ConcurrentHashMap使用分段锁技术,使得在保证线程安全的同时,提供了较好的并发性能。 2. **同步访问**...
此外,在使用keySet方法获取Map集合中的元素时,我们还需要注意Map集合中的元素是否可以被修改,如果Map集合中的元素可以被修改,那么使用keySet方法将会抛出ConcurrentModificationException异常。因此,在实际应用...
这里的“元素类型”是数据源中的元素类型,“变量名”是你在循环中使用的临时变量,而“数据源”可以是数组或实现了`Iterable`接口的集合。 1. **遍历数组**: 对于数组,你可以这样做: ```java int[] numbers ...
它支持协变、逆变等高级特性,但在使用中需要注意类型擦除带来的影响,并通过通配符、类型边界等方法解决泛型在使用中可能遇到的问题。 类型擦除(Type Erasure)是Java泛型的一种机制,它在运行时移除泛型信息,以...
在Java编程中,程序员们常常会遇到一些常见的错误,这些错误可能在初学者和经验丰富的开发者之间都有所体现。本文将探讨Java程序员们最常犯的10个错误,并给出相应的纠正方法,这对于准备Java面试或者日常开发工作都...
4. `java.util.concurrent.atomic`包:提供了一组原子类,如`AtomicInteger`、`AtomicLong`等,它们的原子操作在多线程环境中避免了锁的开销。 遵循以上规则,可以显著提高多线程程序的并发性能和稳定性。然而,...