java中并发容器J.U.C怎么用

发布时间:2021-10-19 15:52:31 作者:柒染
来源:亿速云 阅读:152
# Java中并发容器J.U.C怎么用

## 一、J.U.C概述

Java并发工具包(Java Util Concurrent,简称J.U.C)是Java 5引入的一组高质量并发编程工具,位于`java.util.concurrent`包下。它提供了比传统同步容器更高效的线程安全实现,主要包含以下组件:

1. **并发集合**:替代同步集合的高性能线程安全容器
2. **同步器**:如CountDownLatch、CyclicBarrier等
3. **线程池**:Executor框架
4. **原子变量**:AtomicInteger等
5. **锁机制**:ReentrantLock等

## 二、核心并发容器详解

### 1. ConcurrentHashMap

#### 基本用法
```java
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key1", 1);
map.putIfAbsent("key1", 2); // 不存在才放入
int value = map.get("key1");

特性

高级API

// 原子更新
map.compute("key1", (k, v) -> v == null ? 1 : v + 1);

// 搜索
String result = map.search(1, (k, v) -> v > 100 ? k : null);

// 批量操作
map.forEach(2, (k, v) -> System.out.println(k + ":" + v));

2. CopyOnWriteArrayList

适用场景

读多写少的并发场景,如事件监听器列表

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item1"); // 写时复制
String item = list.get(0); // 无锁读取

注意事项

3. ConcurrentLinkedQueue

无界非阻塞队列实现:

ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
queue.offer("element1");
String head = queue.poll();

特点: - CAS实现的无锁算法 - 高性能但size()方法需要遍历

4. BlockingQueue家族

ArrayBlockingQueue

有界阻塞队列:

BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
queue.put("item"); // 阻塞插入
String item = queue.take(); // 阻塞取出

LinkedBlockingQueue

可选有界/无界:

// 无界队列
BlockingQueue<String> unbounded = new LinkedBlockingQueue<>();

// 有界队列
BlockingQueue<String> bounded = new LinkedBlockingQueue<>(100);

PriorityBlockingQueue

带优先级的无界队列:

BlockingQueue<Task> queue = new PriorityBlockingQueue<>(11, Comparator.comparing(Task::getPriority));

DelayQueue

延迟队列:

class DelayItem implements Delayed {
    // 必须实现getDelay()和compareTo()
}

DelayQueue<DelayItem> delayQueue = new DelayQueue<>();

5. ConcurrentSkipListMap

跳表实现的并发有序Map:

ConcurrentSkipListMap<Integer, String> map = new ConcurrentSkipListMap<>();
map.put(3, "three");
map.put(1, "one");
// 保证遍历顺序是1->3

三、使用场景对比

容器 特点 适用场景
ConcurrentHashMap 高并发K-V存储 缓存、共享数据存储
CopyOnWriteArrayList 读多写少 监听器列表、配置数据
ConcurrentLinkedQueue 无界非阻塞队列 任务队列、消息传递
ArrayBlockingQueue 有界阻塞队列 生产者-消费者模式
LinkedBlockingQueue 可选有界队列 线程池工作队列
ConcurrentSkipListMap 有序并发Map 需要排序的并发数据

四、性能优化技巧

  1. 合理选择容器类型

    • 读远大于写:CopyOnWrite系列
    • 严格吞吐量要求:ConcurrentHashMap
    • 顺序访问需求:ConcurrentSkipListMap
  2. 避免热点竞争

// 不好的做法 - 所有修改竞争同一键
map.compute("counter", (k, v) -> v + 1);

// 更好的做法 - 分散热点
map.compute(Thread.currentThread().getName(), (k, v) -> v + 1);
  1. 批量操作减少锁开销
// 使用批量操作方法
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.putAll(otherMap);

// 而不是循环put
for(Map.Entry<String, Integer> entry : otherMap.entrySet()) {
    map.put(entry.getKey(), entry.getValue()); // 多次获取锁
}

五、常见问题解决方案

1. 复合操作问题

问题代码:

if(!map.containsKey(key)) {
    map.put(key, value); // 非原子操作
}

解决方案:

map.putIfAbsent(key, value);

// 或者使用compute
map.compute(key, (k, v) -> v == null ? value : v);

2. 弱一致性问题

迭代时可能不反映最新修改:

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// ...添加元素...

// 迭代期间可能有修改
for(String key : map.keySet()) {
    // 可能看到部分修改
}

解决方案:

// 如果需要强一致性,需要额外同步
synchronized(map) {
    for(String key : map.keySet()) {
        // ...
    }
}

3. 内存一致性问题

确保修改对其他线程可见:

// 不安全的发布
class UnsafeHolder {
    public static ConcurrentHashMap unsafeMap;
}

// 正确做法
class SafeHolder {
    private static final ConcurrentHashMap safeMap = new ConcurrentHashMap();
    
    public static ConcurrentHashMap getMap() {
        return safeMap;
    }
}

六、高级特性应用

1. ConcurrentHashMap的并行操作

Java 8引入的并行操作方法:

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// 并行forEach
map.forEach(2, (k, v) -> System.out.println(k + ":" + v));

// 并行reduce
int sum = map.reduceValues(2, Integer::sum);

// 并行search
String key = map.search(2, (k, v) -> v > 100 ? k : null);

2. 自定义BlockingQueue

实现生产者-消费者模式:

class CustomBlockingQueue<T> {
    private final Queue<T> queue = new LinkedList<>();
    private final int limit;
    
    public CustomBlockingQueue(int limit) {
        this.limit = limit;
    }
    
    public synchronized void put(T item) throws InterruptedException {
        while(queue.size() == limit) {
            wait();
        }
        queue.add(item);
        notifyAll();
    }
    
    public synchronized T take() throws InterruptedException {
        while(queue.isEmpty()) {
            wait();
        }
        T item = queue.remove();
        notifyAll();
        return item;
    }
}

七、最佳实践总结

  1. 优先使用J.U.C而非同步容器:如用ConcurrentHashMap代替Collections.synchronizedMap
  2. 了解各容器的实现原理:如ConcurrentHashMap的分段锁机制
  3. 避免不必要的同步:利用并发容器的原子方法
  4. 注意迭代器的弱一致性:不要在迭代过程中做结构性修改
  5. 合理配置容量:特别是BlockingQueue实现类
  6. 利用Java 8+的新API:如ConcurrentHashMap的流式操作

八、实战案例:电商库存系统

public class InventorySystem {
    private final ConcurrentHashMap<String, AtomicInteger> inventory;
    
    public InventorySystem() {
        inventory = new ConcurrentHashMap<>();
    }
    
    // 线程安全的库存扣减
    public boolean deduct(String productId, int quantity) {
        return inventory.computeIfPresent(productId, (k, v) -> {
            int remaining = v.get() - quantity;
            return remaining >= 0 ? new AtomicInteger(remaining) : v;
        }) != null;
    }
    
    // 批量查询
    public Map<String, Integer> getInventoryStatus(Set<String> productIds) {
        return productIds.stream()
            .collect(Collectors.toMap(
                id -> id,
                id -> inventory.getOrDefault(id, new AtomicInteger(0)).get()
            ));
    }
}

通过合理使用J.U.C并发容器,可以构建出高性能、线程安全的并发系统。开发者应当根据具体场景选择合适的容器,并充分理解其特性才能发挥最大效益。 “`

推荐阅读:
  1. 如何在Java中实现同步容器和并发容器
  2. 如何理解Java 容器中并发容器的源码分析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java j.u.c

上一篇:如何解决springboot做的controller服务时每隔一段时间后第一次请求耗时特别长

下一篇:Maven的-pl -am -amd参数是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》