【多线程】Java多线程基础(12)- 使用Concurrent集合

74 阅读3分钟

Concurrent(并发)集合

什么是Concurrent集合

Concurrent集合是Java中的一组线程安全的集合类,它们可以被多个线程同时访问而不需要显式的同步操作。相比于传统的集合类,使用Concurrent集合可以更容易地实现高性能、高并发的程序。

总之,它算是将一般集合变为线程安全的一个工具类

以下是几个常用的Concurrent集合类:

  1. ConcurrentHashMap:线程安全的哈希表,支持高并发的读和写操作。它使用分段锁来实现高并发性和可伸缩性,不同的段可以被不同的线程同时修改,从而减小了锁的粒度,提高了并发性能。
  2. ConcurrentLinkedQueue:线程安全的队列,支持高并发的入队和出队操作。它使用基于链表的数据结构来实现队列,通过CAS操作来保证线程安全性。
  3. CopyOnWriteArrayList:线程安全的动态数组,支持高并发的读操作和低并发的写操作。它使用一种写时复制的策略,在写操作时复制一份原数组,并对副本进行修改,保证了读操作的线程安全性。由于每次写操作都会复制整个数组,因此写操作的性能较低。
  4. ConcurrentSkipListMap:线程安全的有序映射表,支持高并发的读和写操作。它使用跳表的数据结构来实现有序映射表,同时使用分层锁来实现高并发性和可伸缩性,不同的层可以被不同的线程同时修改,从而减小了锁的粒度,提高了并发性能。

使用Concurrent集合可以使代码更加简洁、高效和安全,但需要注意的是,虽然Concurrent集合可以保证线程安全,但并不保证操作的原子性和一致性

下面解释为什么不保证操作的原子性与一致性:

在使用ConcurrentHashMap时,虽然它可以支持高并发的读和写操作,并且可以保证线程安全,但是如果需要进行复合操作,例如检查一个键是否存在并且在不存在时插入一个值,这个操作并不能保证原子性和一致性。因为即使在检查键是否存在的时候,另外一个线程可能已经插入了相同的键值对,从而导致这个复合操作执行时出现错误。

在这种情况下,需要使用其他的同步机制来保证操作的正确性。例如,可以使用Locksynchronized关键字来保证复合操作的原子性和一致性。这样可以避免多个线程同时访问时的竞态条件,并保证操作的正确性。

怎么使用Concurrent集合

和一般的集合类使用一样,不过Concurrent集合类是线程安全的。

import java.util.concurrent.CopyOnWriteArrayList;

class Counter {
    private CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
    // 在集合里添加一个数。
    public void increment() {
        // 和一般集合用法一样
        list.add(1);
    }
    // 返回count的值
    public int getCount() {
        int sum = 0;
        for (int i : list) {
            sum += i;
        }
        return sum;
    }
}

 class Worker implements Runnable {
    private Counter counter;
    public Worker(Counter counter) {
        this.counter = counter;
    }
    public void run() {
        for (int i = 0; i < 1000; i++) {
            counter.increment();
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Worker(counter));
            threads[i].start();
        }
        // 这段代码是让Main线程等一等它们,然后才输出结果
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println("Count: " + counter.getCount());
    }
}