java putifabsent方法详解
介绍
在Java的ConcurrentHashMap
类中,有一个非常有用的方法putIfAbsent
。这个方法的作用是当指定的键不存在时,向ConcurrentHashMap
中插入一个指定的键值对。如果在插入操作执行时,已经存在了指定的键,那么这个插入操作将会失败。这个方法的签名如下所示:
V putIfAbsent(K key, V value)
这个方法返回的是一个与指定键关联的旧值,如果键不存在,则返回null
。这个方法在多线程的环境下非常有用,因为它提供了线程安全的插入操作。
用法示例
我们来看一个简单的示例来演示putIfAbsent
方法的用法:
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put("name", "Alice");
String oldValue = map.putIfAbsent("name", "Bob");
System.out.println(oldValue);
在上面的代码中,我们首先创建了一个ConcurrentHashMap
实例map
。然后,我们使用put
方法向map
中插入一个键值对:("name", "Alice")
。接着,我们使用putIfAbsent
方法尝试插入一个键值对:("name", "Bob")
。因为键"name"
已经存在于map
中,所以插入操作会失败。最后,我们打印出了putIfAbsent
方法的返回值,即与指定键"name"
关联的旧值。由于插入操作失败,返回值应该是null
。下面是代码的输出:
Alice
putIfAbsent
方法的实现
在ConcurrentHashMap
类中,putIfAbsent
方法的实现与put
方法的实现紧密相关。当putIfAbsent
方法被调用时,它会首先尝试去插入指定的键值对,如果插入操作成功,就直接返回null
。如果插入操作失败,它就会尝试去获取与指定键相关联的旧值,并返回这个旧值。
下面是putIfAbsent
方法的代码实现:
public class ConcurrentHashMap<K, V> {
private final Node<K, V>[] table;
public V putIfAbsent(K key, V value) {
int hash = key.hashCode(); // 生成键的哈希值
int index = (hash & (table.length - 1)); // 根据哈希值确定键在table中的索引
for (Node<K, V> node = table[index]; node != null; node = node.next) {
K k;
// 如果指定键已经存在,直接返回旧值
if (node.hash == hash && ((k = node.key) == key || key.equals(k))) {
return node.value;
}
}
// 如果指定键不存在,则进行插入操作
return put(key, value, hash, index);
}
// 插入操作的具体实现
private synchronized V put(K key, V value, int hash, int index) {
// ...
}
// ...
}
上面的代码片段只是ConcurrentHashMap
类的部分代码实现,目的是为了展示putIfAbsent
方法的大致逻辑。在实际的代码实现中,ConcurrentHashMap
类还包含其他的方法和变量,用于处理并发的读写操作,以保证线程安全性。
多线程示例
putIfAbsent
方法在多线程的场景中特别有用。假设有多个线程同时插入键值对到ConcurrentHashMap
中,我们可以使用putIfAbsent
方法来保证只有一个线程能够成功地插入。其他线程在插入时会发现指定的键已经存在,从而插入操作失败。
下面是一个多线程示例,演示了putIfAbsent
方法在保证线程安全性方面的作用:
import java.util.concurrent.ConcurrentHashMap;
public class PutIfAbsentDemo {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 创建多个线程并发插入键值对
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
map.putIfAbsent("key", i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 1000; i < 2000; i++) {
map.putIfAbsent("key", i);
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(map.get("key"));
}
}
在上面的示例中,我们创建了一个ConcurrentHashMap
实例map
,并使用两个线程并发地插入键值对到map
中。由于putIfAbsent
方法会保证只有一个线程能够成功地插入键值对,即使线程2尝试插入了键值对("key", 1000)
到map
中,因为键已经存在,插入操作仍然会失败。最终,我们打印出与指定键"key"
关联的值,输出应该是0
。
总结
在本文中,我们详细地介绍了Java中的putIfAbsent
方法。我们首先对这个方法的作用进行了简要的说明,然后给出了一个使用示例来演示它的用法。为了更好地理解putIfAbsent
方法的实现细节,我们还给出了一个简化的伪代码。最后,我们展示了putIfAbsent
方法在多线程场景中的实际应用。