Java并发集合

  • 阻塞队列(BlockingQueue)
  • 核心方法
  • 阻塞队列类型
  • 类结构
  • ArrayBlockingQueue
  • DelayBlockingQueue
  • TransferQueue
  • LinkedTransferQueue
  • PriorityBlockingQueue
  • SynchronousQueue
  • 并发字典(ConcurrentHashMap)
  • 核心方法
  • ConcurrentHashMap
  • 并发字典(ConcurrentSkipListMap)
  • 并发集合(ConcurrentSkipListSet)
  • 写时复制集合
  • 类结构
  • CopyOnWriteArrayList
  • CopyOnWriteArraySet


阻塞队列(BlockingQueue)

在Java的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。

多线程环境中,通过队列可以很容易实现数据共享,比如经典的“生产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的情况呢?理想情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。然而,在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。好在此时,强大的concurrent包横空出世了,而他也给我们带来了强大的BlockingQueue。(在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒)

作为BlockingQueue的使用者,我们再也不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了。

核心方法

入列:

方法签名

是否加锁

是否阻塞

阻塞是否可中断

其它

public boolean add(E e)



不涉及

继承自AbstractQueue

public boolean offer(E e)




插入成功后唤醒等待线程

public void put(E e) throws InterruptedException


有界队列阻塞,无界队列不阻塞


插入成功后唤醒等待线程

public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException


有界队列阻塞,无界队列不阻塞


插入成功后唤醒等待线程

出列:

方法签名

是否加锁

是否阻塞

阻塞是否可中断

其它

public E poll()



不涉及

元素出列成功后唤醒等待线程

public E take() throws InterruptedException




元素出列成功后唤醒等待线程

public E poll(long timeout, TimeUnit unit) throws InterruptedException




元素出列成功后唤醒等待线程

public E peek()



不涉及

只读取,不删除元素

public int drainTo(Collection<? super E> c)



不涉及

一次出列全部元素

int drainTo(Collection<? super E> c, int maxElements)



不涉及

一次出列指定个数的元素

阻塞队列类型

类名

介绍

是否有界

是否加锁

底层数据结构

ArrayBlockingQueue

是一个用数组实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。支持公平锁和非公平锁。



ArrayList

LinkedBlockingQueue

一个由链表结构组成的有界队列,此队列的长度为Integer.MAX_VALUE。此队列按照先进先出的顺序进行排序。

构造函数指定有界或无界


LinkedList

PriorityBlockingQueue

一个支持线程优先级排序的无界队列,默认自然序进行排序,也可以自定义实现compareTo()方法来指定元素排序规则,不能保证同优先级元素的顺序。




DelayQueue

一个实现PriorityBlockingQueue实现延迟获取的无界队列,在创建元素时,可以指定多久才能从队列中获取当前元素。只有延时期满后才能从队列中获取元素。(DelayQueue可以运用在以下应用场景:1.缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。2.定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。)




SynchronousQueue

一个不存储元素的阻塞队列,每一个put操作必须等待take操作,否则不能添加元素。支持公平锁和非公平锁。SynchronousQueue的一个使用场景是在线程池里。Executors.newCachedThreadPool()就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。




LinkedTransferQueue

一个由链表结构组成的无界阻塞队列,相当于其它队列,LinkedTransferQueue队列多了transfer和tryTransfer方法。



LinkedList

LinkedBlockingDeque

一个由链表结构组成的双向阻塞队列。队列头部和尾部都可以添加和移除元素,多线程并发时,可以将锁的竞争最多降到一半。



LinkedList

类结构


«interface» 
    
      BlockingQueue 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +remainingCapacity() : int 
    
      +remove(Object o) : boolean 
    
      +contains(Object o) : boolean 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      ArrayBlockingQueue 
    
      lock : ReentrantLock 
    
      notEmpty : Condition 
    
      notFull : Condition 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +poll() : E 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +peek() : E 
    
      +size() : int 
    
      +remainingCapacity() : int 
    
      +remove(Object o) : boolean 
    
      +contains(Object o) : boolean 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +toString() : String 
    
      +clear() 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +iterator() : Iterator<E> 
    
      ReentrantLock 
    
      «interface» 
    
      Condition 
    
      LinkedBlockingQueue 
    
      lock : ReentrantLock 
    
      notEmpty : Condition 
    
      notFull : Condition 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +poll() : E 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +peek() : E 
    
      +size() : int 
    
      +remainingCapacity() : int 
    
      +remove(Object o) : boolean 
    
      +contains(Object o) : boolean 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +toString() : String 
    
      +clear() 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +iterator() : Iterator<E> 
    
      PriorityBlockingQueue 
    
      lock : ReentrantLock 
    
      notEmpty : Condition 
    
      notFull : Condition 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +poll() : E 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +peek() : E 
    
      +size() : int 
    
      +remainingCapacity() : int 
    
      +remove(Object o) : boolean 
    
      +contains(Object o) : boolean 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +toString() : String 
    
      +clear() 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +iterator() : Iterator<E> 
    
      SynchronousQueue 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +poll() : E 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +peek() : E 
    
      +size() : int 
    
      +remainingCapacity() : int 
    
      +remove(Object o) : boolean 
    
      +contains(Object o) : boolean 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +toString() : String 
    
      +clear() 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +iterator() : Iterator<E> 
    
      Queue 
    
      AbstractQueue 
    
 

      DelayQueue 
    
      lock : ReentrantLock 
    
      notEmpty : Condition 
    
      notFull : Condition 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +poll() : E 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +peek() : E 
    
      +size() : int 
    
      +remainingCapacity() : int 
    
      +remove(Object o) : boolean 
    
      +contains(Object o) : boolean 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +toString() : String 
    
      +clear() 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +iterator() : Iterator<E> 
    
      «interface» 
    
      Delayed 
    
      long getDelay 
    
      «interface» 
    
      BlockingDeque 
    
      +addFirst(E e) 
    
      +addLast(E e) 
    
      +offerFirst(E e) : boolean 
    
      +offerLast(E e) : boolean 
    
      +putFirst(E e) 
    
      +putLast(E e) 
    
      +offerFirst(E e, long timeout, TimeUnit unit) : boolean 
    
      +offerLast(E e, long timeout, TimeUnit unit) : boolean 
    
      +takeFirst() : E 
    
      +takeLast() : E 
    
      +pollFirst(long timeout, TimeUnit unit) : E 
    
      +pollLast(long timeout, TimeUnit unit) : E 
    
      +removeFirstOccurrence(Object o) : boolean 
    
      +removeLastOccurrence(Object o) : boolean 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +remove() : E 
    
      +poll() : E 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +element() : E 
    
      +peek() : E 
    
      +remove(Object o) : boolean 
    
      +contains(Object o) : boolean 
    
      +size() : int 
    
      +iterator() : Iterator<E> 
    
      +push(E e) 
    
      LinkedBlockingDeque 
    
      lock : ReentrantLock 
    
      notEmpty : Condition 
    
      notFull : Condition 
    
      +addFirst(E e) 
    
      +addLast(E e) 
    
      +offerFirst(E e) : boolean 
    
      +offerLast(E e) : boolean 
    
      +putFirst(E e) 
    
      +putLast(E e) 
    
      +offerFirst(E e, long timeout, TimeUnit unit) : boolean 
    
      +offerLast(E e, long timeout, TimeUnit unit) : boolean 
    
      +removeFirst() : E 
    
      +removeLast() : E 
    
      +pollFirst() : E 
    
      +pollLast() : E 
    
      +takeFirst() : E 
    
      +takeLast() : E 
    
      +pollFirst(long timeout, TimeUnit unit) : E 
    
      +pollLast(long timeout, TimeUnit unit) : E 
    
      +getFirst() : E 
    
      +getLast() : E 
    
      +peekFirst() : E 
    
      +peekLast() : E 
    
      +removeFirstOccurrence(Object o) : boolean 
    
      +removeLastOccurrence(Object o) : boolean 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +remove() : E 
    
      +poll() : E 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +element() : E 
    
      +peek() : E 
    
      +remainingCapacity() : int 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +push(E e) 
    
      +pop() : E 
    
      +remove(Object o) : boolean 
    
      +size() : int 
    
      +contains(Object o) : boolean 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +toString() : String 
    
      +clear() 
    
      +iterator() : Iterator<E> 
    
      +descendingIterator() : Iterator<E> 
    
      «interface» 
    
      TransferQueue 
    
      +tryTransfer(E e) : boolean 
    
      +transfer(E e) 
    
      +tryTransfer(E e, long timeout, TimeUnit unit) : boolean 
    
      +hasWaitingConsumer() : boolean 
    
      +getWaitingConsumerCount() : int 
    
      LinkedTransferQueue 
    
      +add(E e) : boolean 
    
      +offer(E e) : boolean 
    
      +put(E e) 
    
      +offer(E e, long timeout, TimeUnit unit) : boolean 
    
      +poll() : E 
    
      +take() : E 
    
      +poll(long timeout, TimeUnit unit) : E 
    
      +peek() : E 
    
      +size() : int 
    
      +remainingCapacity() : int 
    
      +remove(Object o) : boolean 
    
      +contains(Object o) : boolean 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +toString() : String 
    
      +clear() 
    
      +drainTo(Collection<? super E> c) : int 
    
      +drainTo(Collection<? super E> c, int maxElements) : int 
    
      +iterator() : Iterator<E> 
    
      +tryTransfer(E e) : boolean 
    
      +transfer(E e) 
    
      +tryTransfer(E e, long timeout, TimeUnit unit) : boolean 
    
      +hasWaitingConsumer() : boolean 
    
      +getWaitingConsumerCount() : int 
    
      Queue 
    
      BlockingQueue 
    
      AbstractQueue 
    
      Comparable 
    
      PriorityQueue


ArrayBlockingQueue

package com.qupeng.concurrent.blockingqueue;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestArrayBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(2);
        Producer producer1 = new Producer(blockingQueue);
        Producer producer2 = new Producer(blockingQueue);
        Producer producer3 = new Producer(blockingQueue);
        Producer producer4 = new Producer(blockingQueue);
        Producer producer5 = new Producer(blockingQueue);
        Consumer consumer = new Consumer(blockingQueue);
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        executorService.execute(producer1);
        executorService.execute(producer2);
        executorService.execute(producer3);
        executorService.execute(producer4);
        executorService.execute(producer5);
        executorService.execute(consumer);

        Thread.sleep(10 * 1000);

        producer1.isRunning = false;
        producer2.isRunning = false;
        producer3.isRunning = false;
        producer4.isRunning = false;
        producer5.isRunning = false;
        consumer.isRunning = false;
        executorService.shutdownNow();
    }
    static class Consumer implements Runnable {
        private boolean isRunning = true;
        private BlockingQueue<String> blockingQueue;
        public Consumer(BlockingQueue<String> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            try {
                while(isRunning) {
                    String product = blockingQueue.take();
                    System.out.println(String.format("%60sConsume : %s", " ", product));
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {

            }
        }
    }

    static class Producer implements Runnable {
        private boolean isRunning = true;
        private AtomicInteger index = new AtomicInteger(1);
        private BlockingQueue<String> blockingQueue;
        public Producer(BlockingQueue<String> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            try {
                while (isRunning) {
                    String product = "product-" + Thread.currentThread().getId() + "-" + index.getAndAdd(1);
                    if (blockingQueue.size() == 2) {
                        System.out.println("Blocking queue is full. Producer " + Thread.currentThread().getId() + " will be blocked");
                    }
                    blockingQueue.put(product);
                    System.out.println("Producer " + Thread.currentThread().getId() + " was awakened, produced : " + product);
                }
            } catch (InterruptedException e) {

            }
        }
    }
}

DelayBlockingQueue

package com.qupeng.concurrent.blockingqueue;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestDelayBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Product> blockingQueue = new DelayQueue<>();
        Producer producer1 = new Producer(blockingQueue, 5000);
        Producer producer2 = new Producer(blockingQueue, 4000);
        Producer producer3 = new Producer(blockingQueue, 3000);
        Producer producer4 = new Producer(blockingQueue, 2000);
        Producer producer5 = new Producer(blockingQueue, 1000);
        Consumer consumer = new Consumer(blockingQueue);
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        executorService.execute(producer1);
        executorService.execute(producer2);
        executorService.execute(producer3);
        executorService.execute(producer4);
        executorService.execute(producer5);
        executorService.execute(consumer);

        Thread.sleep(10 * 1000);

        producer1.isRunning = false;
        producer2.isRunning = false;
        producer3.isRunning = false;
        producer4.isRunning = false;
        producer5.isRunning = false;
        consumer.isRunning = false;
        executorService.shutdownNow();
    }

    static class Product implements Delayed {
        private String name;
        private long start = System.currentTimeMillis();
        private long delay;
        public Product(String name, long delay) {
            this.name = name;
            this.delay = delay;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert((start + delay) - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed o) {
            long diff = this.delay - ((Product)o).delay;
            if (diff > 0) {
                return 1;
            } else if (diff == 0) {
                return 0;
            } else {
                return -1;
            }
        }
    }

    static class Consumer implements Runnable {
        private boolean isRunning = true;
        private BlockingQueue<Product> blockingQueue;
        public Consumer(BlockingQueue<Product> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            try {
                while(isRunning) {
                    Product product = blockingQueue.take();
                    System.out.println(String.format("%60sConsume : %s delayed; ", " ", product.name, product.delay));
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {

            }
        }
    }

    static class Producer implements Runnable {
        private boolean isRunning = true;
        private AtomicInteger index = new AtomicInteger(1);
        private BlockingQueue<Product> blockingQueue;
        private long delay;

        public Producer(BlockingQueue<Product> blockQueue, long delay) {
            this.blockingQueue = blockQueue;
            this.delay = delay;
        }

        @Override
        public void run() {
            try {
                while (isRunning) {
                    Product product = new Product(Thread.currentThread().getId() + "-" + index.getAndAdd(1), delay);
                    blockingQueue.put(product);
                    System.out.println("Producer " + Thread.currentThread().getId() + " produced : " + product.name);
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {

            }
        }
    }
}

TransferQueue

TransferQueue接口定义了非阻塞方式的入队和出队方法

方法签名

说明

boolean tryTransfer(E e)

如果有消费线程阻塞在队列上,那么将数据传送给消费线程并返回ture;否则返回false

void transfer(E e) throws InterruptedException

如果有消费线程阻塞在队列上,那么将数据传送给消费线程;否则阻塞直到有消费线程成功接收数据

boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException

如果有消费线程阻塞在队列上,那么将数据传送给消费线程并返回ture;否则阻塞指定的时间,然后返回false

boolean hasWaitingConsumer()

是否有阻塞的消费线程

int getWaitingConsumerCount()

获取阻塞的消费线程的数目

LinkedTransferQueue

package com.qupeng.concurrent.blockingqueue;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestLinkedTransferQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new LinkedTransferQueue();
        ProducerNotBlocked producer = new ProducerNotBlocked(blockingQueue);
        ProducerBlocked producer1 = new ProducerBlocked(blockingQueue);
        Consumer consumer = new Consumer(blockingQueue);
        Consumer consumer1 = new Consumer(blockingQueue);
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        executorService.execute(producer);
        executorService.execute(producer1);
        executorService.execute(consumer);
        executorService.execute(consumer1);

        Thread.sleep(10 * 1000);

        producer.isRunning = false;
        producer1.isRunning = false;
        consumer.isRunning = false;
        consumer1.isRunning = false;
        executorService.shutdownNow();
    }
    static class Consumer implements Runnable {
        private boolean isRunning = true;
        private BlockingQueue<String> blockingQueue;
        public Consumer(BlockingQueue<String> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            try {
                while(isRunning) {
                    String product = blockingQueue.take();
                    System.out.println(String.format("%60sConsume : %s", " ", product));
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {

            }
        }
    }

    static class ProducerNotBlocked implements Runnable {
        private AtomicInteger index = new AtomicInteger(1);
        private BlockingQueue<String> blockingQueue;
        private boolean isRunning = true;
        public ProducerNotBlocked(BlockingQueue<String> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            TransferQueue transferQueue = (TransferQueue)blockingQueue;
            while (isRunning) {
                System.out.println(String.format("There are %d waiting consumers", transferQueue.getWaitingConsumerCount()));
                String product = "product-" + Thread.currentThread().getId() + "-" + index.getAndAdd(1);
                System.out.println("Try to transfer product to queue...");
                if (transferQueue.hasWaitingConsumer() && transferQueue.tryTransfer(product)) {
                    System.out.println(String.format("Producer %s produced : %s", Thread.currentThread().getId(), product));
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {

                }
            }
        }
    }

    static class ProducerBlocked implements Runnable {
        private AtomicInteger index = new AtomicInteger(1);
        private BlockingQueue<String> blockingQueue;
        private boolean isRunning = true;
        public ProducerBlocked(BlockingQueue<String> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            TransferQueue transferQueue = (TransferQueue)blockingQueue;
            while (isRunning) {
                try {
                    System.out.println(String.format("There are %d waiting consumers", transferQueue.getWaitingConsumerCount()));
                    String product = "product-" + Thread.currentThread().getId() + "-" + index.getAndAdd(1);
                    transferQueue.transfer(product);
                    System.out.println(String.format("Producer %s produced : %s", Thread.currentThread().getId(), product));
                    Thread.sleep(500);
                } catch (InterruptedException e) {

                }
            }
        }
    }
}

PriorityBlockingQueue

package com.qupeng.concurrent.blockingqueue;

import java.util.Comparator;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestPriorityBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Product> blockingQueue = new PriorityBlockingQueue<>(2, new Comparator<Product>() {
            @Override
            public int compare(Product o1, Product o2) {
                return Integer.valueOf(o2.name.split("-")[0]).compareTo(Integer.valueOf(o1.name.split("-")[0]));
            }
        });
        Producer producer1 = new Producer(blockingQueue);
        Producer producer2 = new Producer(blockingQueue);
        Producer producer3 = new Producer(blockingQueue);
        Producer producer4 = new Producer(blockingQueue);
        Producer producer5 = new Producer(blockingQueue);
        Consumer consumer = new Consumer(blockingQueue);
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        executorService.execute(producer1);
        executorService.execute(producer2);
        executorService.execute(producer3);
        executorService.execute(producer4);
        executorService.execute(producer5);
        executorService.execute(consumer);

        Thread.sleep(10 * 1000);

        producer1.isRunning = false;
        producer2.isRunning = false;
        producer3.isRunning = false;
        producer4.isRunning = false;
        producer5.isRunning = false;
        consumer.isRunning = false;
        executorService.shutdownNow();
    }

    static class Product implements Comparable {
        private String name;
        public Product(String name) {
            this.name = name;
        }

        @Override
        public int compareTo(Object o) {
            Product prod1 = (Product)o;
            return Integer.valueOf(this.name.split("-")[0]).compareTo(Integer.valueOf(prod1.name.split("-")[0]));
        }
    }

    static class Consumer implements Runnable {
        private boolean isRunning = true;
        private BlockingQueue<Product> blockingQueue;
        public Consumer(BlockingQueue<Product> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            try {
                while(isRunning) {
                    Product product = blockingQueue.take();
                    System.out.println(String.format("%60sConsume : %s", " ", product.name));
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {

            }
        }
    }

    static class Producer implements Runnable {
        private boolean isRunning = true;
        private AtomicInteger index = new AtomicInteger(1);
        private BlockingQueue<Product> blockingQueue;
        public Producer(BlockingQueue<Product> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            try {
                while (isRunning) {
                    Product product = new Product(Thread.currentThread().getId() + "-" + index.getAndAdd(1));
                    blockingQueue.put(product);
                    System.out.println("produce : " + product.name);
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {

            }
        }
    }
}

SynchronousQueue

package com.qupeng.concurrent.blockingqueue;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestSynchronousQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> blockingQueue = new SynchronousQueue();
        Producer producer1 = new Producer(blockingQueue);
        Producer producer2 = new Producer(blockingQueue);
        Producer producer3 = new Producer(blockingQueue);
        Producer producer4 = new Producer(blockingQueue);
        Producer producer5 = new Producer(blockingQueue);
        Consumer consumer = new Consumer(blockingQueue);
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        executorService.execute(producer1);
        executorService.execute(producer2);
        executorService.execute(producer3);
        executorService.execute(producer4);
        executorService.execute(producer5);
        executorService.execute(consumer);

        Thread.sleep(10 * 1000);

        producer1.isRunning = false;
        producer2.isRunning = false;
        producer3.isRunning = false;
        producer4.isRunning = false;
        producer5.isRunning = false;
        consumer.isRunning = false;
        executorService.shutdownNow();
    }
    static class Consumer implements Runnable {
        private boolean isRunning = true;
        private BlockingQueue<String> blockingQueue;
        public Consumer(BlockingQueue<String> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(3000);
                while(isRunning) {
                    String product = blockingQueue.take();
                    System.out.println(String.format("%60sConsume : %s", " ", product));
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {

            }
        }
    }

    static class Producer implements Runnable {
        private boolean isRunning = true;
        private AtomicInteger index = new AtomicInteger(1);
        private BlockingQueue<String> blockingQueue;
        public Producer(BlockingQueue<String> blockQueue) {
            this.blockingQueue = blockQueue;
        }

        @Override
        public void run() {
            try {
                while (isRunning) {
                    String product = "product-" + Thread.currentThread().getId() + "-" + index.getAndAdd(1);
                    System.out.println("Producer " + Thread.currentThread().getId() + " try to put : " + product);
                    blockingQueue.put(product);
                    System.out.println("Producer " + Thread.currentThread().getId() + " was awakened, produced : " + product);
                }
            } catch (InterruptedException e) {

            }
        }
    }
}

并发字典(ConcurrentHashMap)

核心方法

并发Map的统一接口ConcurrentMap中新增了4个方法,以CAS的方式来提升并发的效率。

方法签名

说明

V putIfAbsent(K key, V value)

如果key对应的值value不存在就put,且返回null。如果key对应的值value已存在,则返回已存在的值,且value不能为null,否则会报空指针异常。

boolean remove(Object key, Object value)

仅当key存在并且value等于实参时,删除并返回true;否则返回false。

boolean replace(K key, V oldValue, V newValue)

仅当key存在并且value等于实参时,覆盖value并返回true;否则返回false。

V replace(K key, V value)

如果key对应的value存在就put,且返回原有值;否则直接返回null。

HashMap 在高并发下会出现链表环,从而导致程序出现死循环。高并发下避免 HashMap 出问题的方法有两种,一是使用 HashTable,二是使用 Collections.syncronizedMap。但是这两种方法的性能都能差。因为这两个在执行读写操作时都是将整个集合加锁,导致多个线程无法同时读写集合。高并发下的 HashMap 出现的问题就需要 ConcurrentHashMap 来解决了。
ConcurrentHashMap 中有一个 Segment 的概念。Segment 本身就相当于一个 HashMap 对象。同 HashMap 一样,Segment 包含一个 HashEntry 数组,数组中的每一个 HashEntry 既是一个键值对,也是一个链表的头节点。
ConcurrentHashMap 集合中有 2 的N次方个 Segment 对象,共同保存在一个名为 segments 的数组当中。

锁分段技术:
首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据仍能被其他线程访问。
ConcurrentHashMap 提供了与 Hashtable 和 SynchronizedMap 不同的锁机制。Hashtable 中采用的锁机制是一次锁住整个 hash 表,从而在同一时刻只能由一个线程对其进行操作;而 ConcurrentHashMap 中则是一次锁住一个桶。
ConcurrentHashMap 默认将 hash 表分为16个桶,诸如 get、put、remove 等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。


«interface» 
    
      ConcurrentMap 
    
      +putIfAbsent(K key, V value) : V 
    
      +remove(Object key, Object value) : boolean 
    
      +replace(K key, V oldValue, V newValue) : boolean 
    
      +replace(K key, V value) : V 
    
      ConcurrentHashMap 
    
      DEFAULT_INITIAL_CAPACITY=16 : int 
    
      DEFAULT_LOAD_FACTOR = 0.75f : float 
    
      DEFAULT_CONCURRENCY_LEVEL = 16 : int 
    
      MAXIMUM_CAPACITY = 1 << 30 : int 
    
      MIN_SEGMENT_TABLE_CAPACITY = 2 : int 
    
      MAX_SEGMENTS = 1 << 16 : int 
    
      RETRIES_BEFORE_LOCK = 2 : int 
    
      segmentMask : int 
    
      segmentShift : int 
    
      segments : Segment<K,V>[] 
    
      keySet : Set<K> 
    
      entrySet: Set<Entry> 
    
      values : Collection<V> 
    
      ~entryAt(HashEntry<K,V>[] tab, int i) : HashEntry<KV> 
    
      +size() : int 
    
      +isEmpty() : boolean 
    
      +containsValue(Object value) : boolean 
    
      +containsKey(Object key) : boolean 
    
      +get(Object key) : V 
    
      +contains(Object value) : boolean 
    
      +put(K key, V value) : V 
    
      +putIfAbsent(K key, V value) : V 
    
      +remove(Object key) : V 
    
      +putAll(Map<? extends K, ? extends V> m) 
    
      +remove(Object key, Object value) : boolean 
    
      +replace(K key, V oldValue, V newValue) : boolean 
    
      +replace(K key, V value) : V 
    
      +clear() 
    
      +keySet() : Set<K> 
    
      +values() : Collection<V> 
    
      +entrySet() : Set<Entry> 
    
      +keys() : Enumeration<K> 
    
      +elements() : Enumeration<V> 
    
      -rehash(HashEntry<K,V> node) 
    
      HashEntry 
    
      ~setNext(HashEntry<K,V> n) 
    
      Segment 
    
      table : HashEntry<K,V>[] 
    
      «abstract» 
    
      HashIterator 
    
      +hasNext() : boolean 
    
      ~nextEntry() : Entry<KV> 
    
      +remove() 
    
      Map 
    
      Serializable 
    
      AbstractMap 
    
      ReentrantLock

ConcurrentHashMap

如果5个线程同时更新HashMap中的同一个key:counter对应的value,每一个线程都给这个值加10,最终的counter值应该为50. 如何实现?
普通HashMap实现,最终的值肯定远小于50:

package com.qupeng.concurrent.map;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestHashMap {
    public static void main(String[] args) throws InterruptedException {
        Map<String, Integer> map = new HashMap(8, (float) 0.75);
        ExecutorService executorService = Executors.newCachedThreadPool();
        Producer producer1 = new Producer(map);
        Producer producer2 = new Producer(map);
        Producer producer3 = new Producer(map);
        Producer producer4 = new Producer(map);
        Producer producer5 = new Producer(map);
        executorService.execute(producer1);
        executorService.execute(producer2);
        executorService.execute(producer3);
        executorService.execute(producer4);
        executorService.execute(producer5);
        while (!producer1.isStop || !producer2.isStop || !producer3.isStop || !producer4.isStop || !producer5.isStop) {
            Thread.sleep(1000);
        }
        System.out.println("The total number in the map : " + map.get("counter"));
        executorService.shutdownNow();
    }

    static class Producer implements Runnable {

        private Map<String, Integer> map;

        private volatile boolean isStop = false;

        public Producer(Map concurrentMap) {
            this.map = concurrentMap;
        }

        @Override
        public void run() {
            int i = 10;
            while (i-- > 0) {
                Integer oldValue = map.get("counter");
                if (null == oldValue) {
                    map.put("counter", 1);
                } else {
                    map.put("counter", oldValue + 1);
                }
            }
            isStop = true;
        }
    }
}

synchronized+CountDownLatch实现,执行时间31毫秒:

package com.qupeng.concurrent.map;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestHashMapV2 {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        final CountDownLatch latch = new CountDownLatch(5);
        Map<String, Integer> map = new HashMap(8, (float) 0.75);
        ExecutorService executorService = Executors.newCachedThreadPool();
        Producer producer1 = new Producer(map, latch);
        Producer producer2 = new Producer(map, latch);
        Producer producer3 = new Producer(map, latch);
        Producer producer4 = new Producer(map, latch);
        Producer producer5 = new Producer(map, latch);
        executorService.execute(producer1);
        executorService.execute(producer2);
        executorService.execute(producer3);
        executorService.execute(producer4);
        executorService.execute(producer5);
        latch.await();
        System.out.println("The total number in the map : " + map.get("counter"));
        System.out.println("Execution time : " + (System.currentTimeMillis() - start));
        executorService.shutdownNow();
    }

    static class Producer implements Runnable {
        private Map<String, Integer> map;
        private CountDownLatch latch;
        public Producer(Map concurrentMap, CountDownLatch latch) {
            this.map = concurrentMap;
            this.latch = latch;
        }

        @Override
        public void run() {
            int i = 10;
            while (i-- > 0) {
                synchronized(this.map) {
                    Integer oldValue = map.get("counter");
                    if (null == oldValue) {
                        map.put("counter", 1);
                    } else {
                        map.put("counter", oldValue + 1);
                    }
                }
            }
            latch.countDown();
        }
    }
}

ConcurrentHashMap实现:

package com.qupeng.concurrent.map;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestConcurrentHashMap {
    public static void main(String[] args) throws InterruptedException {
        ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap(8, (float) 0.75);
        ExecutorService executorService = Executors.newCachedThreadPool();
        Producer producer1 = new Producer(concurrentMap);
        Producer producer2 = new Producer(concurrentMap);
        Producer producer3 = new Producer(concurrentMap);
        Producer producer4 = new Producer(concurrentMap);
        Producer producer5 = new Producer(concurrentMap);
        executorService.execute(producer1);
        executorService.execute(producer2);
        executorService.execute(producer3);
        executorService.execute(producer4);
        executorService.execute(producer5);
        while (!producer1.isStop || !producer2.isStop || !producer3.isStop || !producer4.isStop || !producer5.isStop) {
            Thread.sleep(1000);
        }
        System.out.println("The total number in the map : " + concurrentMap.get("counter"));
        executorService.shutdownNow();
    }

    static class Producer implements Runnable {
        private ConcurrentMap<String, Integer> concurrentMap;
        private volatile boolean isStop = false;
        public Producer(ConcurrentMap concurrentMap) {
            this.concurrentMap = concurrentMap;
        }

        @Override
        public void run() {
            int i = 10;
            while (i-- > 0) {
                while (true) {
                    Integer oldValue = concurrentMap.get("counter");
                    if (null == oldValue) {
                        if (null == concurrentMap.putIfAbsent("counter", 1)) {
                            break;
                        }
                    } else {
                        if (concurrentMap.replace("counter", oldValue, oldValue + 1)) {
                            break;
                        }
                    }
                }
            }
            isStop = true;
        }
    }
}

ConcurrentHashMap+AtomicInteger+CountDownLatch,执行时间16毫秒

package com.qupeng.concurrent.map;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestConcurrentHashMapV2 {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        final CountDownLatch latch = new CountDownLatch(5);
        ConcurrentMap<String, AtomicInteger> concurrentMap = new ConcurrentHashMap(8, (float) 0.75);
        ExecutorService executorService = Executors.newCachedThreadPool();
        Producer producer1 = new Producer(concurrentMap, latch);
        Producer producer2 = new Producer(concurrentMap, latch);
        Producer producer3 = new Producer(concurrentMap, latch);
        Producer producer4 = new Producer(concurrentMap, latch);
        Producer producer5 = new Producer(concurrentMap, latch);
        executorService.execute(producer1);
        executorService.execute(producer2);
        executorService.execute(producer3);
        executorService.execute(producer4);
        executorService.execute(producer5);
        latch.await();
        System.out.println("The total number in the map : " + concurrentMap.get("counter").intValue());
        System.out.println("Execution time : " + (System.currentTimeMillis() - start));
        executorService.shutdownNow();
    }

    static class Producer implements Runnable {
        private ConcurrentMap<String, AtomicInteger> concurrentMap;
        private CountDownLatch cutDownLatch;
        public Producer(ConcurrentMap concurrentMap, CountDownLatch latch) {
            this.concurrentMap = concurrentMap;
            this.cutDownLatch = latch;
        }

        @Override
        public void run() {
            int i = 10;
            while (i-- > 0) {
                AtomicInteger oldValue = concurrentMap.get("counter");
                if (null == oldValue) {
                    AtomicInteger newValue = new AtomicInteger(1);
                    oldValue = concurrentMap.putIfAbsent("counter", newValue);
                    if (null != oldValue) {
                        i++;
                    }
                } else {
                    oldValue.incrementAndGet();
                }
            }
            cutDownLatch.countDown();
        }
    }
}

并发字典(ConcurrentSkipListMap)

ConcurrentSkipListMap提供了一种线程安全的并发访问的排序映射表,使用场景对标TreeMap。内部是SkipList(跳表)结构实现,在理论上能够O(log(n))时间内完成查找、插入、删除操作。
跳表是一种随机化的数据结构,其数据元素按照key(键)排序,所以跳表是有序的集合,跳表为了提高查找、插入和删除操作的性能,在原有的有序链表之上随机的让一部分数据元素分布到更多层链表中,从而在查找、插入和删除操作时可以跳过一些不可能涉及的数据元素节点,从而提高效率。

目前常用的key-value数据结构有三种:Hash表、红黑树、SkipList,它们各自有着不同的优缺点(不考虑删除操作):

  • Hash表:插入、查找最快,为O(1);如使用链表实现则可实现无锁;数据有序化需要显式的排序操作。
  • 红黑树:插入、查找为O(logn),但常数项较小;无锁实现的复杂性很高,一般需要加锁;数据天然有序。
  • SkipList:插入、查找为O(logn),但常数项比红黑树要大;底层结构为链表,可无锁实现;数据天然有序。

如果要实现一个key-value结构,需求的功能有插入、查找、迭代、修改,那么首先Hash表就不是很适合了,因为迭代的时间复杂度比较高;而红黑树的插入很可能会涉及多个结点的旋转、变色操作,因此需要在外层加锁,这无形中降低了它可能的并发度。而SkipList底层是用链表实现的,可以实现为lock free,同时它还有着不错的性能(单线程下只比红黑树略慢),非常适合用来实现我们需求的那种key-value结构。
LevelDB、Reddis的底层存储结构就是用的SkipList。


«interface» 
    
      ConcurrentMap 
    
      +putIfAbsent(K key, V value) : V 
    
      +remove(Object key, Object value) : boolean 
    
      +replace(K key, V oldValue, V newValue) : boolean 
    
      +replace(K key, V value) : V 
    
      «interface» 
    
      ConcurrentNavigableMap 
    
      +subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) : ConcurrentNavigableMap<KV> 
    
      +headMap(K toKey, boolean inclusive) : ConcurrentNavigableMap<KV> 
    
      +tailMap(K fromKey, boolean inclusive) : ConcurrentNavigableMap<KV> 
    
      +subMap(K fromKey, K toKey) : ConcurrentNavigableMap<KV> 
    
      +headMap(K toKey) : ConcurrentNavigableMap<KV> 
    
      +tailMap(K fromKey) : ConcurrentNavigableMap<KV> 
    
      +descendingMap() : ConcurrentNavigableMap<KV> 
    
      +navigableKeySet() : NavigableSet<K> 
    
      +keySet() : NavigableSet<K> 
    
      +descendingKeySet() : NavigableSet<K> 
    
      ConcurrentSkipListMap 
    
      -findPredecessor(Comparable<? super K> key) : Node<KV> 
    
      -findNode(Comparable<? super K> key) : Node<KV> 
    
      -doGet(Object okey) : V 
    
      -doPut(K kkey, V value, boolean onlyIfAbsent) : V 
    
      -randomLevel() : int 
    
      -insertIndex(Node<K,V> z, int level) 
    
      -addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel) 
    
      -doRemove(Object okey, Object value) : V 
    
      -tryReduceLevel() 
    
      +clone() : ConcurrentSkipListMap<KV> 
    
      +containsKey(Object key) : boolean 
    
      +get(Object key) : V 
    
      +put(K key, V value) : V 
    
      +remove(Object key) : V 
    
      +containsValue(Object value) : boolean 
    
      +size() : int 
    
      +isEmpty() : boolean 
    
      +clear() 
    
      +keySet() : NavigableSet<K> 
    
      +navigableKeySet() : NavigableSet<K> 
    
      +values() : Collection<V> 
    
      +entrySet() : Set<Entry> 
    
      +descendingMap() : ConcurrentNavigableMap<KV> 
    
      +descendingKeySet() : NavigableSet<K> 
    
      +equals(Object o) : boolean 
    
      +putIfAbsent(K key, V value) : V 
    
      +remove(Object key, Object value) : boolean 
    
      +replace(K key, V oldValue, V newValue) : boolean 
    
      +replace(K key, V oldValue, V newValue) : boolean 
    
      +replace(K key, V value) : V 
    
      super K 
    
      +lastKey() : K 
    
      +subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) : ConcurrentNavigableMap<KV> 
    
      +headMap(K toKey, boolean inclusive) : ConcurrentNavigableMap<KV> 
    
      +tailMap(K fromKey, boolean inclusive) : ConcurrentNavigableMap<KV> 
    
      +subMap(K fromKey, K toKey) : ConcurrentNavigableMap<KV> 
    
      +headMap(K toKey) : ConcurrentNavigableMap<KV> 
    
      +tailMap(K fromKey) : ConcurrentNavigableMap<KV> 
    
      +lowerEntry(K key) : Entry<KV> 
    
      +lowerKey(K key) : K 
    
      +floorEntry(K key) : Entry<KV> 
    
      +floorKey(K key) : K 
    
      +ceilingEntry(K key) : Entry<KV> 
    
      +ceilingKey(K key) : K 
    
      +higherEntry(K key) : Entry<KV> 
    
      +higherKey(K key) : K 
    
      +firstEntry() : Entry<KV> 
    
      +lastEntry() : Entry<KV> 
    
      +pollFirstEntry() : Entry<KV> 
    
      +pollLastEntry() : Entry<KV> 
    
      Node<K,V> 
    
      key : K 
    
      value : Object 
    
      next : Node<KV> 
    
      -UNSAFE : Unsafe 
    
      -valueOffset : long 
    
      -nextOffset : long 
    
      ~casValue(Object cmp, Object val) : boolean 
    
      ~casNext(Node<K,V> cmp, Node<K,V> val) : boolean 
    
      ~isMarker() : boolean 
    
      ~isBaseHeader() : boolean 
    
      ~appendMarker(Node<K,V> f) : boolean 
    
      ~helpDelete(Node<K,V> b, Node<K,V> f) 
    
      ~getValidValue() : V 
    
      ~createSnapshot() : SimpleImmutableEntry<KV> 
    
      Unsafe 
    
      Index<K,V> 
    
      node : Node<KV> 
    
      down : Index<KV> 
    
      right : Index<KV> 
    
      -UNSAFE : Unsafe 
    
      -rightOffset : long 
    
      ~casRight(Index<K,V> cmp, Index<K,V> val) : boolean 
    
      ~indexesDeletedNode() : boolean 
    
      ~link(Index<K,V> succ, Index<K,V> newSucc) : boolean 
    
      ~unlink(Index<K,V> succ) : boolean 
    
      HeadIndex<K,V> 
    
      level : int 
    
      Map 
    
      Cloneable 
    
      Serializable 
    
      AbstractMap


package com.qupeng.concurrent.map;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

public class TestConcurrentSkipListMap {
    public static void main(String args[]) {
        ConcurrentSkipListMap<Integer, String> concurrentSkipListMap = new ConcurrentSkipListMap();
        int i = 50;
        while (i-- > 0) {
            concurrentSkipListMap.put(i, String.valueOf(i));
        }
        StringBuilder stringBuilder = new StringBuilder();
        Iterator<Map.Entry<Integer, String>> iterator = concurrentSkipListMap.entrySet().iterator();
        while (iterator.hasNext()) {
            stringBuilder.append(iterator.next().getKey()).append(",");
        }
        System.out.println(stringBuilder.toString());

        stringBuilder = new StringBuilder();
        ConcurrentNavigableMap concurrentNavigableMap = concurrentSkipListMap.descendingMap();
        iterator = concurrentNavigableMap.entrySet().iterator();
        while (iterator.hasNext()) {
            stringBuilder.append(iterator.next().getKey()).append(",");
        }
        System.out.println(stringBuilder.toString());
    }
}

并发集合(ConcurrentSkipListSet)

ConcurrentSkipListSet是线程安全的有序的集合,适用于高并发的场景。
ConcurrentSkipListSet应用场景对标TreeSet,它们都是有序的集合。但是,第一,它们的线程安全机制不同,TreeSet是非线程安全的,而ConcurrentSkipListSet是线程安全的。第二,ConcurrentSkipListSet是通过ConcurrentSkipListMap实现的,而TreeSet是通过TreeMap实现的。

ConcurrentSkipListSet 
    
      -m : ConcurrentNavigableMap<E,Object> 
    
      +clone() : ConcurrentSkipListSet<E> 
    
      +size() : int 
    
      +isEmpty() : boolean 
    
      +contains(Object o) : boolean 
    
      +iterator() : Iterator<E> 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +add(E e) : boolean 
    
      +remove(Object o) : boolean 
    
      +containsAll(Collection<?> c) : boolean 
    
      +addAll(Collection<? extends E> c) : boolean 
    
      +retainAll(Collection<?> c) : boolean 
    
      +removeAll(Collection<?> c) : boolean 
    
      +clear() 
    
      +equals(Object o) : boolean 
    
      +lower(E e) : E 
    
      +floor(E e) : E 
    
      +ceiling(E e) : E 
    
      +higher(E e) : E 
    
      +pollFirst() : E 
    
      +pollLast() : E 
    
      +first() : E 
    
      +last() : E 
    
      +iterator() : Iterator<E> 
    
      +descendingSet() : NavigableSet<E> 
    
      +descendingIterator() : Iterator<E> 
    
      +subSet(E fromElement, boolean fromInclusive,E toElement, boolean toInclusive) : NavigableSet<E> 
    
      +headSet(E toElement, boolean inclusive) : NavigableSet<E> 
    
      +tailSet(E fromElement, boolean inclusive) : NavigableSet<E> 
    
      +subSet(E fromElement, E toElement) : SortedSet<E> 
    
      +headSet(E toElement) : SortedSet<E> 
    
      +tailSet(E fromElement) : SortedSet<E> 
    
      super E 
    
      NavigableSet 
    
      Cloneable 
    
      Serializable 
    
      AbstractSet 
    
      ConcurrentSkipListMap 
    
     1 
   
     1


写时复制集合

类结构


CopyOnWriteArrayList 
    
      lock : ReentrantLock 
    
      -array : Object[] 
    
      +CopyOnWriteArrayList(E[] toCopyIn) 
    
      +size() : int 
    
      +isEmpty() : boolean 
    
      +contains(Object o) : boolean 
    
      +iterator() : Iterator<E> 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +add(E e) : boolean 
    
      +remove(Object o) : boolean 
    
      +containsAll(Collection<?> c) : boolean 
    
      +addAll(Collection<? extends E> c) : boolean 
    
      +addAll(int index, Collection<? extends E> c) : boolean 
    
      +removeAll(Collection<?> c) : boolean 
    
      +retainAll(Collection<?> c) : boolean 
    
      +clear() 
    
      +equals(Object o) : boolean 
    
      +hashCode() : int 
    
      +get(int index) : E 
    
      +set(int index, E element) : E 
    
      +add(int index, E element) 
    
      +remove(int index) : E 
    
      +indexOf(Object o) : int 
    
      +indexOf(E e, int index) : int 
    
      +lastIndexOf(Object o) : int 
    
      +lastIndexOf(E e, int index) : int 
    
      +listIterator() : ListIterator<E> 
    
      +listIterator(int index) : ListIterator<E> 
    
      +subList(int fromIndex, int toIndex) : List<E> 
    
      +clone() : Object 
    
      +addIfAbsent(E e) : boolean 
    
      +addAllAbsent(Collection<? extends E> c) : int 
    
      +toString() : String 
    
      COWIterator 
    
      snapshot : Object[] 
    
      cursor : int 
    
      COWSubList 
    
      CopyOnWriteArraySet 
    
      -al : CopyOnWriteArrayList<E> 
    
      +size() : int 
    
      +isEmpty() : boolean 
    
      +contains(Object o) : boolean 
    
      +iterator() : Iterator<E> 
    
      +toArray() : Object[] 
    
      +toArray(T[] a) : T[] 
    
      +add(E e) : boolean 
    
      +remove(Object o) : boolean 
    
      +containsAll(Collection<?> c) : boolean 
    
      +addAll(Collection<? extends E> c) : boolean 
    
      +retainAll(Collection<?> c) : boolean 
    
      +removeAll(Collection<?> c) : boolean 
    
      +clear() 
    
      +equals(Object o) : boolean 
    
      +hashCode() : int 
    
      List 
    
      RandomAccess 
    
      Cloneable 
    
      Serializable 
    
      AbstractSet 
    
     1 
   
     1

CopyOnWriteArrayList

CopyOnWriteArrayList这是一个ArrayList的线程安全的变体,其原理大概可以通俗的理解为:初始化的时候只有一个容器,很长一段时间,这个容器数据、数量等没有发生变化的时候,大家(多个线程),都是读取(假设这段时间里只发生读取的操作)同一个容器中的数据,所以这样大家读到的数据都是唯一、一致、安全的,但是后来有人往里面增加了一个数据,这个时候CopyOnWriteArrayList 底层实现添加的原理是先copy出一个容器(可以简称副本),再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。

  1. 写数组的拷贝,支持高效率并发且是线程安全的,读操作无锁的ArrayList。所有可变操作都是通过对底层数组进行一次新的复制来实现。
  2. 适合使用在读操作远远大于写操作的场景里,比如缓存。它不存在扩容的概念,每次写操作都要复制一个副本,在副本的基础上修改后改变Array引用。CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差。
  3. 合适读多写少的场景,不过这类慎用 ,因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。
    CopyOnWriteArrayList 有几个缺点:
  4. 由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc。
    (1、young gc :年轻代(Young Generation):对象被创建时,内存的分配首先发生在年轻代(大对象可以直接被创建在年老代),大部分的对象在创建后很快就不再使用,因此很快变得不可达,于是被年轻代的GC机制清理掉(IBM的研究表明,98%的对象都是很快消亡的),这个GC机制被称为Minor GC或叫Young GC。
  5. 年老代(Old Generation):对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时,将执行Major GC,也叫 Full GC
  6. 不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求;
    CopyOnWriteArrayList示例:
package com.qupeng.concurrent.list;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestCopyOnWriteArrayList {
    public static void main(String[] args) throws InterruptedException {
//        List<String> list = new ArrayList();
        List<String> list = new CopyOnWriteArrayList<>();
        Reader reader1 = new Reader(list);
        Reader reader2 = new Reader(list);
        Reader reader3 = new Reader(list);
        Reader reader4 = new Reader(list);
        Reader reader5 = new Reader(list);
        Writer writer = new Writer(list);
        ExecutorService executorService = Executors.newFixedThreadPool(6);
        executorService.execute(reader1);
        executorService.execute(reader2);
        executorService.execute(reader3);
        executorService.execute(reader4);
        executorService.execute(reader5);
        executorService.execute(writer);

        Thread.sleep(5 * 1000);
        reader1.isRunning = false;
        reader2.isRunning = false;
        reader3.isRunning = false;
        reader4.isRunning = false;
        reader5.isRunning = false;
        writer.isRunning = false;
        executorService.shutdownNow();
    }

    static class Writer implements Runnable {
        private int value = 0;
        private boolean isRunning = true;
        private List<String> list;
        public Writer(List<String> list) {
            this.list = list;
        }

        @Override
        public void run() {
            while (isRunning) {
//                System.out.println(String.format("%60sAdd to list : %s", " ", value++));
                list.add(String.valueOf(value));
            }
        }
    }

    static class Reader implements Runnable {
        boolean isRunning = true;
        private List<String> list;
        public Reader(List<String> list) {
            this.list = list;
        }

        @Override
        public void run() {
            while (isRunning) {
                Iterator<String> iter = list.iterator();
                while (iter.hasNext()) {
                    System.out.println("Reader " + Thread.currentThread().getId() + " get from list : " + iter.next());
                }
            }
        }
    }
}

如果将CopyOnWriteArrayList换成普通的ArrayList,将会抛出如下异常:

Exception in thread "pool-1-thread-4" Exception in thread "pool-1-thread-5" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
	at java.util.ArrayList$Itr.next(ArrayList.java:791)
	at com.qupeng.concurrent.list.TestCopyOnWriteArrayList$Reader.run(TestCopyOnWriteArrayList.java:67)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:724)

CopyOnWriteArraySet