一、setifabsent方法的介绍
setifabsent方法是ConcurrentMap接口中的一种方法,它可以在ConcurrentMap中添加一个键-值对,但只有在该键不存在时,才能添加成功。如果该键已经存在,则setifabsent方法不会添加该键-值对。
/** * 如果指定的键不存在,则将指定的值与键关联。如果该键已经存在,则此方法不执行任何操作并返回当前的值。 * * @param key 键 * @param value 值 * @return 可能为null的之前的价值,如果没有值映射,则为null */ V putIfAbsent(K key, V value);
和put方法的不同点在于setifabsent方法只有在键不存在时才会添加值,而put方法无论键是否存在都会添加。
二、setifabsent方法的使用场景
setifabsent方法最适用于多线程应用场景,特别是在ConcurrentMap中,多线程同时访问并修改同一个键时,使用setifabsent方法可以保证操作的原子性,避免多个线程同时添加相同的键-值对。
另外,setifabsent方法的返回值为之前该键的旧值,如果该键不存在,则返回null。这个特性可以用于实现一些有用的功能,比如计数器和缓存。
三、使用示例
1. 线程安全的计数器
假设我们需要统计一个网站的访问次数,考虑以下这段代码:
ConcurrentHashMap<String, Integer> counter = new ConcurrentHashMap(); public void count(String page) { if (counter.containsKey(page)) { counter.put(page, counter.get(page) + 1); } else { counter.put(page, 1); } }
count方法中使用了containsKey、put和get方法,虽然这段代码看起来没什么问题,但由于ConcurrentHashMap是一个线程安全的容器,在多线程并发访问的情况下,会存在竞态条件,从而导致结果不准确。
为了保证计数器的正确性,我们可以使用setifabsent方法,将上面的代码修改为以下形式:
ConcurrentHashMap<String, Integer> counter = new ConcurrentHashMap(); public void count(String page) { Integer count = counter.putIfAbsent(page, 1); if (count != null) { counter.put(page, count + 1); } }
在使用setifabsent方法后,只有一个线程可以添加相同的键-值对,其他线程会被阻塞,直到添加完成,这样计数器的结果就是准确的了。
2. 线程安全的缓存
如果我们需要实现一个缓存,在多线程并发访问的情况下,也需要保证缓存的正确性和性能。假设我们使用ConcurrentHashMap作为缓存容器,代码如下:
ConcurrentHashMap<String, String> cache = new ConcurrentHashMap(); public String get(String key) { String value = cache.get(key); if (value == null) { value = expensiveOperation(key); cache.put(key, value); } return value; } private String expensiveOperation(String key) { // some expensive operation }
get方法中使用了get、put和null判断,这段代码在单线程环境下没有问题,但在多线程并发访问的情况下,会存在竞态条件,从而导致缓存结果不准确。
为了保证缓存的正确性和性能,我们可以使用setifabsent方法,将上面的代码修改为以下形式:
ConcurrentHashMap<String, String> cache = new ConcurrentHashMap(); public String get(String key) { String value = cache.get(key); if (value == null) { String newValue = expensiveOperation(key); value = cache.putIfAbsent(key, newValue); if (value == null) { value = newValue; } } return value; } private String expensiveOperation(String key) { // some expensive operation }
在使用setifabsent方法后,只有一个线程可以添加相同的键-值对,其他线程会被阻塞,直到添加完成,这样缓存的结果就是准确的了。另外,由于ConcurrentHashMap是线程安全的,所以使用setifabsent方法可以保证缓存的性能。
原创文章,作者:UDSGR,如若转载,请注明出处:https://www.506064.com/n/371795.html