Java 并发集合 详解

本文介绍了Java中用于并发编程的各类集合,包括阻塞式和非阻塞式的队列、优先级队列、延时队列及线程安全的映射表。同时探讨了原子变量的概念与使用案例,如AtomicLong和AtomicIntegerArray,展示了如何利用这些工具来构建高效且线程安全的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java 提供两类适用于并发应用的集合 :
1> 阻塞式集合 : 当集合已满或为空时,被调用的添加或移除方法不能立即执行,此时这个线程阻塞,一直到该方法可以被成功执行
2> 非阻塞式集合 : 如果方法不能被立即执行,则返回 null 或抛出异常,但是调用这个方法的线程不会被阻塞


ConcurrentLinkedDeque 类
非阻塞式线程安全列表,线程安全的双向链表
public int size() : 返回链表中的元素量,但可能数据不是真实的

public E getFirst()、public E getLast() : 返回元素中的第一个、最后一个数据,且数据不会移除,如果列表为空将抛出 NoSuchElementException

public E peek()、public E peekFirst()、public E peekLast() : 返回元素中的第一个、第一个、最后一个数据,没有数据时返回 null

public E poll()、public E pollFirst()、public E pollLast() : 返回并移除第一个、第一个、最后一个数据,没有数据时返回 null

public E remove()、public E removeFirst()、public E removeLast() : 返回并移除第一个、第一个、最后一个数据,如果列表为空将抛出 NoSuchElementException

public class AddTask implements Runnable {
    private ConcurrentLinkedDeque<String> list;
    public AddTask(ConcurrentLinkedDeque<String> list) {
        this.list = list;
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        for (int i = 0; i < 10000; i++) {
            list.add(name + ": Element " + i);
        }
    }
}

public class PollTask implements Runnable {
    private ConcurrentLinkedDeque<String> list;
    public PollTask(ConcurrentLinkedDeque<String> list) {
        this.list = list;
    }
    @Override
    public void run() {
        for (int i = 0; i < 5000; i++) {
            list.pollFirst();
            list.pollLast();
        }
    }
    public static void main(String[] args) {
        ConcurrentLinkedDeque<String> list = new ConcurrentLinkedDeque<>();
        Thread[] threads = new Thread[100];
        for (int i = 0; i < threads.length; i++) {
            AddTask addTask = new AddTask(list);
            threads[i] = new Thread(addTask);
            threads[i].start();
        }
        System.out.printf("Main: %d AddTask threads have been launched\n", threads.length);
        try {
            for (int i = 0; i < threads.length; i++) {
                threads[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("Main: Size of the List: %d\n", list.size());
        for (int i = 0; i < threads.length; i++) {
            PollTask pollTask = new PollTask(list);
            threads[i] = new Thread(pollTask);
            threads[i].start();
        }
        System.out.printf("Main: %d PollTask threads have been launched\n", threads.length);
        for (int i = 0; i < threads.length; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.printf("Main: Size of the list: %d\n", list.size());
    }
}


LinkedBlockingDeque 类
阻塞式线程安全列表,队列已满或为空操作不会立即执行

public LinkedBlockingDeque(int capacity) : 创建固定容量的 LinkedBlockingDeque 对象

public void put(E e) throws InterruptedException、public void putFirst(E e) throws InterruptedException、public void putLast(E e) throws InterruptedException : 向队列中 结尾、开头、结尾 插入元素,如果队列已满,则阻塞线程等待直到列表中有可用空间

public boolean add(E e)、public void addFirst(E e)、public void addLast(E e) : 向队列中 结尾、开头、结尾 插入元素,如果队列已满则抛出 IllegalStateException

public E take() throws InterruptedException、public E takeFirst() throws InterruptedException、public E takeLast() throws InterruptedException : 从列表中移除并返回第一个、第一个、最后一个元素,如果列表为空则线程将会被阻塞等待一直到列表中有元素

public E poll()、public E pollFirst()、public E pollLast() : 从列表中移除并返回第一个、第一个、最后一个元素,如果列表为空则返回 null

public E getFirst()、public E getLast() : 从列表中返回但不移除第一个、最后一个元素,如果列表为空则抛出 NoSuchElementException

public E peek()、public E peekFirst()、public E peekLast() : 取出但不移除队列中第一个、第一个、最后一个元素,如果列表为空则返回 null

public class Client implements Runnable {
    private LinkedBlockingDeque<String> requestList;
    public Client(LinkedBlockingDeque<String> requestList) {
        this.requestList = requestList;
    }
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 5; j++) {
                StringBuilder request = new StringBuilder();
                request.append(i);
                request.append(":");
                request.append(j);
                try {
                    requestList.put(request.toString());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.printf("Client: %s at %s.\n", request, new Date());
            }
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.printf("Clients: End.\n");
    }
    public static void main(String[] args) throws InterruptedException {
        LinkedBlockingDeque<String> list = new LinkedBlockingDeque<>(3);
        Client client = new Client(list);
        Thread thread = new Thread(client);
        thread.start();
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 3; j++) {
                String request = list.take();
                System.out.printf("Main: Request: %s at %s. Size: %d\n", request, new Date(), list.size());
            }
            TimeUnit.MILLISECONDS.sleep(300);
        }
        System.out.printf("Main: End of the program.\n");
    }
}


PriorityBlockingQueue 类
阻塞式数据结构,所有添加的元素必须实现 Comparable 接口用于排序

public PriorityBlockingQueue() : 创建一个默认数量为 11 的队列,队列中的元素根据 Comparable 进行排序

public boolean add(E e) : 向队列中插入一个优先级队列数据

public void put(E e) : 向队列中插入一个优先级队列数据。因为队列是无界的,所以这个方法永远不会阻塞

public int size() : 返回队列中元素的个数,如果队列中元素个数超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE

public E poll() : 移除并返回队列中第一个元素,如果队列为空则返回 null

public E take() throws InterruptedException : 移除并返回队列中第一个元素,如果队列为空则阻塞线程直到有元素取出

public E peek() : 返回但不移除队列中的数据,如果队列中的数据为空则返回 null

public void clear() : 移除队列中所有元素

public class Event implements Comparable<Event> {
    private int thread;
    private int priority;
    public Event(int thread, int priority) {
        this.thread = thread;
        this.priority = priority;
    }
    public int getThread() {
        return thread;
    }
    public int getPriority() {
        return priority;
    }
    @Override
    public int compareTo(Event event) {
        if (this.priority > event.getPriority()) {
            return -1;
        } else if (this.priority < event.getPriority()) {
            return 1;
        }
        return 0;
    }
}

public class Task implements Runnable {
    private int id;
    private PriorityBlockingQueue<Event> queue;
    public Task(int id, PriorityBlockingQueue<Event> queue) {
        this.id = id;
        this.queue = queue;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            Event event = new Event(id, i);
            queue.add(event);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        PriorityBlockingQueue<Event> queue = new PriorityBlockingQueue<>();
        Thread[] taskThreads = new Thread[5];
        for (int i = 0; i < taskThreads.length; i++) {
            Task task = new Task(i, queue);
            taskThreads[i] = new Thread(task);
            taskThreads[i].start();
        }
        for (int i = 0; i < taskThreads.length; i++) {
            taskThreads[i].join();
        }
        System.out.printf("Main: Queue Size: %d\n", queue.size());
        for (int i = 0; i < taskThreads.length * 1000; i++) {
            Event event = queue.poll();
            System.out.printf("Thread %s: Priority %d\n", event.getThread(), event.getPriority());
        }
        System.out.printf("Main: Queue Size: %d\n", queue.size());
        System.out.printf("Main: End of the program\n");
    }
}


DelayQueue 类
用于存放带有激活日期的元素,当调用方法从队列中返回或提取元素时,未来的元素日期将被忽略

DelayQueue 类 中的元素必须继承 Delayed接口,Delayed接口 继承于 Comparable接口因此有 compareTo方法,这样存放的对象具有激活时间,该接口两个方法 :
1> int compareTo(T o) : 如果当前对象延迟值小于参数对象值,将返回一个小于 0 的值;延迟值大于参数对象,返回一个大于0的值;如果两者延迟值相等则返回0
2> long getDelay(TimeUnit unit) : 返回激活日期剩余时间单位

public E poll() : 返回并移除队列头数据,或者队列中没有一个过期延迟对象返回 null

public E take() throws InterruptedException : 返回并移除第一个元素,如果延迟队列中没有元素则一直等待

public int size() : 返回队列中元素的个数,如果队列中元素个数超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。该方法返回元素的总个数,包括活动和非活动元素

public void clear() : 移除队列中所有元素

public boolean offer(E e) : 插入特定元素到延迟队列中

public E peek() : 返回但不移除第一个元素,如果在过期队列中没有元素将返回即将过期的数据,如果依旧没有则返回 null

public class Event implements Delayed {
    private Date startDate;
    public Event(Date startDate) {
        this.startDate = startDate;
    }
    @Override
    public long getDelay(TimeUnit unit) {
        Date now = new Date();
        long diff = startDate.getTime() - now.getTime();
        return unit.convert(diff, TimeUnit.MILLISECONDS);
    }
    @Override
    public int compareTo(Delayed o) {
        long result = this.getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        if (result < 0) { // 当前对象的延迟值小于参数对象值
            return -1;
        } else if (result > 0) { // 当前对象的延迟值大于参数对象值
            return 1;
        }
        return 0; // 两者的延迟值相等
    }
}

public class Task implements Runnable {
    private int id;
    private DelayQueue<Event> queue;
    public Task(int id, DelayQueue<Event> queue) {
        this.id = id;
        this.queue = queue;
    }
    @Override
    public void run() {
        Date now = new Date();
        Date delay = new Date();
        delay.setTime(now.getTime() + (id * 1000));
        System.out.printf("Thread %s: %s\n", id, delay);
        for (int i = 0; i < 100; i++) {
            Event event = new Event(delay);
            queue.add(event);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<Event> queue = new DelayQueue<>();
        Thread[] threads = new Thread[5];
        for (int i = 0; i < threads.length; i++) {
            Task task = new Task(i + 1, queue);
            threads[i] = new Thread(task);
            threads[i].start();
        }
        for (int i = 0; i < threads.length; i++) {
            threads[i].join();
        }
        do {
            int counter = 0;
            Event event;
            do {
                event = queue.poll();
                if (event != null) {
                    counter++;
                }
            } while (event != null);
            System.out.printf("At %s you have read %d events\n", new Date(), counter);
        } while (queue.size() > 0);
    }
}

线程安全可遍历
ConcurrentNavigableMap接口
该接口 public interface ConcurrentNavigableMap<K,V> extends ConcurrentMap<K,V>, NavigableMap<K,V> 有两个存放元素,一个唯一标示元素另一个是其它部分数据

ConcurrentSkipListMap类
ConcurrentSkipListMap类 实现 ConcurrentNavigableMap接口,public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable
ConcurrentSkipListMap类 内部实现是通过一个 Skip List存放数据,Skip List基于并发列表的数据结构
ConcurrentSkipListMap类 插入的元素会使用键值来排序所有元素

public V put(K key, V value) : 在 map 中使用特定的 key 来关联特定的 value,如果 map 之前已经包含这个 key值,则旧值将会被取代

public Map.Entry<K,V> firstEntry() : 返回map中第一个元素,如果map为空返回 null,返回实体不支持 Entry.setValue 方法

public class Contact {
    private String name;
    private String phone;
    public Contact(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }
    public String getName() {
        return name;
    }
    public String getPhone() {
        return phone;
    }
}

public class Task implements Runnable {
    private ConcurrentSkipListMap<String, Contact> map;
    private String id;
    public Task(ConcurrentSkipListMap<String, Contact> map, String id) {
        this.map = map;
        this.id = id;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            Contact contact = new Contact(id, String.valueOf(i + 1000));
            map.put(id + contact.getPhone(), contact);
        }
    }
    public static void main(String[] args) {
        ConcurrentSkipListMap<String, Contact> map = new ConcurrentSkipListMap<>();
        Thread[] threads = new Thread[25];
        int counter = 0;
        for (char i = 'A'; i < 'Z'; i++) {
            Task task = new Task(map, String.valueOf(i));
            threads[counter] = new Thread(task);
            threads[counter].start();
            counter++;
        }
        try {
            for (int i = 0; i < 25; i++) {
                threads[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("Main: Size of the map: %d\n", map.size());
        Map.Entry<String, Contact> element = map.firstEntry();
        Contact contact = element.getValue();
        System.out.printf("Main: First Entry: %s: %s\n", contact.getName(), contact.getPhone());
        element = map.lastEntry();
        contact = element.getValue();
        System.out.printf("Main: Last Entry: %s: %s\n", contact.getName(), contact.getPhone());

        System.out.printf("Main: Submap from A1996 to B1002: \n");
        ConcurrentNavigableMap<String, Contact> submap = map.subMap("A1996", "B1002");
        do {
            element = submap.pollFirstEntry();
            if (element != null) {
                contact = element.getValue();
                System.out.printf("%s: %s\n", contact.getName(), contact.getPhone());
            }
        } while (element != null);
    }
}


ThreadLocalRandom 类
ThreadLocalRandom 类线程本地变量,相比使用共享 Random对象其性能更好

public static ThreadLocalRandom current() : 返回当前线程的 ThreadLocalRandom 对象

public int nextInt(int bound) : 返回介于指定范围的数据,类似方法还有其它

public class TaskLocalRandom implements Runnable {
    public TaskLocalRandom() {
        ThreadLocalRandom.current();
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        for (int i = 0; i < 10; i++) {
            System.out.printf("%s: %d\n", name, ThreadLocalRandom.current().nextInt(10));
        }
    }
    public static void main(String[] args) {
        Thread[] threads = new Thread[3];
        for (int i = 0; i < 3; i++) {
            TaskLocalRandom task = new TaskLocalRandom();
            threads[i] = new Thread(task);
            threads[i].start();
        }
    }
}


原子变量
原子变量不使用锁或其他同步机制来保护对其值的并发访问,所有操作都基于 CAS原子操作,其保证多线程在同一时间操作一个原子变量而不会反升。当一个线程在对原子变量操作时,如果其它线程试图对同一原子变量执行操作,原子变量的实现类提供一套机制来检查操作是否在一步内完成。一般来说,这个操作先获取变量值,然后在本地改变变量的值,然后试图用这个改变的值去替换之前的值。如果之前的值没有被其它线程改变,就可以执行这个替换操作。否则,方法将再执行这个操作。这个操作成为 CAS原子操作(Compare and Set)

AtomicLong 类
public final void set(long newValue) : 设置给定值

public final long getAndAdd(long delta) : 自动给定指定值到当前值,给定值可以为负数

类似的类还有 AtomicBoolean AtomicInteger 等

public class Bank implements Runnable {
    private Account account;
    public Bank(Account account) {
        this.account = account;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            account.subtractAmount(1000);
        }
    }
}

public class Company implements Runnable {
    private Account account;
    public Company(Account account) {
        this.account = account;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            account.addAmount(1000);
        }
    }
}

public class Account {
    private AtomicLong balance;
    public Account() {
        balance = new AtomicLong();
    }
    public long getBalance() {
        return balance.get();
    }
    public void setBalance(long balance) {
        this.balance.set(balance);
    }
    public void addAmount(long amount) {
        this.balance.getAndAdd(amount);
    }
    public void subtractAmount(long amount) {
        this.balance.getAndAdd(-amount);
    }
    public static void main(String[] args) {
        Account account = new Account();
        account.setBalance(1000);
        Company company = new Company(account);
        Thread companyThread = new Thread(company);
        Bank bank = new Bank(account);
        Thread bankThread = new Thread(bank);
        System.out.printf("Account : Initial Balance: %d\n", account.getBalance());
        companyThread.start();
        bankThread.start();
        try {
            companyThread.join();
            bankThread.join();
            System.out.printf("Account : Final Balance: %d\n", account.getBalance());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

原子数组
当实现一个并发应用时,将不可避免地会有多线程共享一个或多个对象的现象,为避免数据不一致问题需要使用同步机制(锁或synchronized关键字) 来保护这些共享属性的访问,但是同步机制存在问题 :
1> 死锁 : 一个线程被阻塞,并且试图获得锁正在被其它线程使用,但其它线程永远不会释放这个锁。这种情况使得应用不会继续执行,永远不会结束
2> 即是只有一个线程访问共享对象,它仍然需要执行必须的代码来获取和释放锁
未解决这种问题,Java引入比较和交换操作(Compare-and-Swap Operation),该操作使用以下三个变量值 :
1> 取得变量值,即变量的旧值
2> 在一个临时变量中修改变量值,即变量的新值
3> 如果上面获得的变量值与当前变量值相等,就用新值替换旧值。如果已有其它线程修改这个变量值,上面获得的变量的旧值就可能与当前变量值不同
采用比较和交换机制不需要使用同步机制,不仅可以避免死锁而且性能更好

Java在原子变量中实现这种机制

AtomicIntegerArray 类
public final int getAndDecrement(int i) : 在指定索引处的元素自动减一

public final int getAndIncrement(int i) : 在指定索引处的元素自动加一

public class Incrementer implements Runnable {
    private AtomicIntegerArray vector;
    public Incrementer(AtomicIntegerArray vector) {
        this.vector = vector;
    }
    @Override
    public void run() {
        for (int i = 0; i < vector.length(); i++) {
            vector.getAndIncrement(i);
        }
    }
}

public class Decrementer implements Runnable {
    private AtomicIntegerArray vector;
    public Decrementer(AtomicIntegerArray vector) {
        this.vector = vector;
    }
    @Override
    public void run() {
        for (int i = 0; i < vector.length(); i++) {
            vector.getAndDecrement(i);
        }
    }
    public static void main(String[] args) {
        final int THREADS = 100;
        AtomicIntegerArray vector = new AtomicIntegerArray(1000);
        Incrementer incrementer = new Incrementer(vector);
        Decrementer decrementer = new Decrementer(vector);
        Thread[] threadIncrementer = new Thread[THREADS];
        Thread[] threadDecrementer = new Thread[THREADS];
        for (int i = 0; i < THREADS; i++) {
            threadIncrementer[i] = new Thread(incrementer);
            threadDecrementer[i] = new Thread(decrementer);
            threadIncrementer[i].start();
            threadDecrementer[i].start();
        }
        try {
            for (int i = 0; i < THREADS; i++) {
                threadIncrementer[i].join();
                threadDecrementer[i].join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0, len = vector.length(); i < len; i++) {
            if (vector.get(i) != 0) {
                System.out.println("Vector[" + i + "] : " + vector.get(i));
            }
        }
        System.out.println("Main: End of the example");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值