常用并发工具、并发容器(并发编程篇)

目录

jdk-api中文手册

常用的并发工具类

常用并发容器

并发容器有哪些分类


jdk-api中文手册

常用的并发工具类

CountDownLatch:允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。

可以理解为:统计xx-x的航班票数,必须等到多个航空公司(多线程)都统计完成后才能汇总展示给用户。

用法:CountDownLatch latch=new CountDownLatch(number),先初始化一个number;再多线程情况下

每调用一次countDown()方法,计数-1直到number为0时,再配合await()使用;如果当前计数大于零,

则当前线程将被禁用以进行线程调度,并处于休眠状态。

常用方法:

  • public void countDown();
    减少锁存器的计数,如果计数达到零,释放所有等待的线程。

    如果当前计数大于零,则它将递减。 如果新计数为零,则所有等待的线程都将被重新启用以进行线程调度。

    如果当前计数等于零,那么没有任何反应。

  • getCount

    public long getCount();

    返回当前计数。

    该方法通常用于调试和测试。

  • await()

public void await() throws InterruptedException;

public boolean await(long timeout,TimeUnit unit) throws InterruptedException;

导致当前线程等到锁存器计数到零,除非线程是interrupted 。

如果当前计数为零,则此方法立即返回。

如果当前计数大于零,则当前线程将被禁用以进行线程调度,并处于休眠状态,直至发生两件事情之一:

示例:

public class CountDownLatchDemo {

    private static CountDownLatch cdl = new CountDownLatch(3);

    private static List<String> tasks = new ArrayList<String>();

    public static void main(String[] args) throws InterruptedException {
        final Thread[] threads = new Thread[3];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    int val = new Random().nextInt(5);
                    TimeUnit.SECONDS.sleep(val);
                    System.out.println(Thread.currentThread().getName() + " :完成任务【 " + val + "个】");
                    tasks.add(Thread.currentThread().getName() + " : " + val);
                    cdl.countDown();
                }
            });
            threads[i].start();
            threads[i].join();
        }
        //await()方法调用会校验cdl.getCount() ==0,为0则返回true,否则线程进入休眠.
        cdl.await();
        tasks.forEach(System.out::println);
    }
}

CyclicBarrier:允许一组线程全部等待彼此达到共同屏障点的同步辅助。

可以理解为:比如马拉松比赛,必须等到所有参赛选手(多线程)都准备好,才能开始跑。

用法:CyclicBarrier barrier = new CyclicBarrier(10); 初始化一个屏障值number=10,初始值=0,每当线程调用一次await()则初始值+1、线程进入休眠状态;当初始值=10时,此刻屏障破损,所有线程再一起执行。

常用方法

public int await() throws

                              InterruptedException,

                              BrokenBarrierException;

等待所有parties已经在这个障碍上调用了await 。

如果当前线程不是最后一个线程,那么它被禁用以进行线程调度,并且处于休眠状态,直到发生下列事情之一:

  • 最后一个线程到达; 要么一些其他线程当前线程为interrupts ; 

  • 要么一些其他线程interrupts其他等待线程之一; 

  • 要么一些其他线程在等待屏障时超时; 

  • 要么其他一些线程在这个屏障上调用reset() 。

public int await(long timeout,
                 TimeUnit unit)
          throws InterruptedException,
                 BrokenBarrierException,
                 TimeoutException

等待所有parties已经在此屏障上调用await ,或指定的等待时间过去。

如果当前线程不是最后一个线程,那么它被禁用以进行线程调度,并且处于休眠状态,直到发生下列事情之一:

  • 最后一个线程到达; 要么
  • 超过指定的超时 要么
  • 一些其他线程当前线程interrupts ; 要么
  • 其他一些线程interrupts其他等待线程; 要么
  • 一些其他线程在等待屏障时超时; 要么
  • 其他一些线程在这个障碍上调用reset() 。
  • isBroken

    public boolean isBroken()

    查询这个障碍是否处于破碎状态。

    结果

    true如果一个或多个参与方因施工或最后一次重置而导致中断或超时,或由于异常而导致屏障动作失败,则从此出现障碍; false否则。

  • reset

    public void reset()

    将屏障重置为初始状态。 如果任何一方正在等待屏障,他们将返回BrokenBarrierException 。 注意,由于其他原因,发生断线的复位可能会复杂化; 线程需要以其他方式重新同步,并选择一个执行重置。 可能更好地为后续使用创建新的屏障。

  • getNumberWaiting

    public int getNumberWaiting()

    返回目前正在等待障碍的各方的数量。 此方法主要用于调试和断言。

    结果

    目前受阻于各方的数量 await()

示例

  public static void main(String[] args) {
        //模拟马拉松跑步
        CyclicBarrier barrier = new CyclicBarrier(10);
        Thread[] player = new Thread[10];
        for (int i = 0; i < player.length; i++) {
            player[i] = new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                    System.out.println(Thread.currentThread().getName() + " ok ");
                    barrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("player:" + Thread.currentThread().getName() + " running ");
            }, "player[" + i + "]");
            player[i].start();
        }
    }

Semaphore:一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,每个acquire()都会阻塞,

直到许可证可用,然后才能使用它。 每个release()添加许可证,潜在地释放阻塞获取方。 但是没有使用实际的

许可证对象; Semaphore只保留可用数量的计数,并相应地执行。

可以理解:停车场业务,比如停车场只有10个车位,每辆车进入停车场则获取一个许可证,当许可证(信号量+1)=10时,不允许停车了(阻塞),当出去一辆车,则释放一个许可证(信号量-1),此刻通知还可以停一辆车,有效的控制停车流量。

使用方法:先初始化一个计数信号量count  :  Semaphore sp=new Semaphore(5);当线程获得调用acquire()获得许可凭证,则count+1,直到count = 5 /此刻如果再有线程调用则不能获取许可进入休眠;当获取许可的线程调用release()则释放资源,其他等待的线程继续获取许可。

常用方法

  • Semaphore

    public Semaphore(int permits)

    创建一个 Semaphore与给定数量的许可证和非公平公平设置。

    参数

    permits - permits的初始许可证。 该值可能为负数,在这种情况下,必须在任何获取被授予之前发布释放。

 

如果当前线程:

 

 

然后InterruptedException被关上,当前线程的中断状态被清除。

 

  • Semaphore

    public void acquire()
                 throws InterruptedException

    从此信号量获取许可证,阻止直到可用,否则线程为interrupted 。

    获得许可证,如果有可用并立即返回,则将可用许可证数量减少一个。

    如果没有可用的许可证,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,直至发生两件事情之一:

  • 一些其他线程调用此信号量的release()方法,当前线程旁边将分配一个许可证; 要么

  • 一些其他线程interrupts当前线程。

  • 在进入该方法时设置了中断状态; 要么

  • interrupted等候许可证,

 

常用并发容器

ConcurrentLinkedDeque:非阻塞式集合(Non-Blocking Collection),这类集合也包括添加和移除数据的方法。如果方法不能立即被执行,则返回null或抛出异常,但是调用这个方法的线程不会被阻塞。

Constructor and Description
ConcurrentLinkedDeque()

构造一个空的德克。

ConcurrentLinkedDeque(Collection<? extends E> c)

构造最初包含给定集合的元素的deque,以集合的迭代器的遍历顺序添加。

boolean add(E e)

在此deque的尾部插入指定的元素。

boolean addAll(Collection<? extends E> c)

按指定集合的迭代器返回的顺序将指定集合中的所有元素追加到该deque的末尾。

void addFirst(E e)

在此deque前面插入指定的元素。

void addLast(E e)

在此deque的末尾插入指定的元素。

void clear()

从这个deque中删除所有的元素。

boolean contains(Object o)

返回 true如果这个deque包含至少一个元素 e ,这样 o.equals(e) 。

E pop()

从这个deque表示的堆栈中弹出一个元素。

void push(E e)

将元素推送到由此deque代表的堆栈(换句话说,在该deque的头部),如果可以立即执行,而不违反容量限制,则抛出 IllegalStateException如果当前没有可用空间)。

E remove()

检索并删除由此deque表示的队列的头(换句话说,该deque的第一个元素)。

boolean remove(Object o)

删除第一个元素 e ,使 o.equals(e) ,如果这样一个元素存在于这个deque。

示例:

 public static void main(String[] args) throws InterruptedException {
        ConcurrentLinkedDeque<String> cld = new ConcurrentLinkedDeque();
        //添加数据
        Thread[] add = new Thread[100];
        for (int i = 0; i < 100; i++) {
            add[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    cld.add(Thread.currentThread().getName() + ":Element " + j);
                }
            });
            add[i].start();
            add[i].join();
        }
        System.out.println("after add size:" + cld.size());

        //移除数据

        Thread[] poll = new Thread[100];
        for (int i = 0; i < 100; i++) {
            poll[i] = new Thread(() -> {
                for (int j = 0; j < 500; j++) {
                    cld.pollLast();
                    cld.pollFirst();
                }
            });
            poll[i].start();
            poll[i].join();
        }
        System.out.println("after poll size:" + cld.size());
    }

LinkedBlockingDeque:阻塞式集合(Blocking Collection),这类集合包括添加和移除数据的方法。当集合已满或为空时,被调用的添加或者移除方法就不能立即被执行,那么调用这个方法的线程将被阻塞,一直到该方法可以被成功执行。

LinkedBlockingDeque()

创建一个 LinkedBlockingDeque ,容量为 Integer.MAX_VALUE 。

LinkedBlockingDeque(Collection<? extends E> c)

创建一个 LinkedBlockingDeque ,容量为 Integer.MAX_VALUE ,最初包含给定集合的元素,以集合的迭代器的遍历顺序添加。

LinkedBlockingDeque(int capacity)

创建一个具有给定(固定)容量的 LinkedBlockingDeque 。

 public static void main(String[] args) throws InterruptedException {
        LinkedBlockingDeque<String> list = new LinkedBlockingDeque();
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < 5; j++) {
                    String str = new String(i + ":" + j);
                    try {
                        list.put(str.toString());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("put: " + str + (new Date()));
                }
            }
        });
        thread.start();
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 5; j++) {
                String str = list.take();
                System.out.println("take: " + str + " size:" + list.size());
                TimeUnit.SECONDS.sleep(2);
            }
        }

    }

 

并发容器有哪些分类

Java并发容器的原理:7大并发容器详解、及使用场景

1.ConcurrentHashMap

对应的非并发容器:HashMap

目标:代替Hashtable、synchronizedMap,支持复合操作

原理:JDK6中采用一种更加细粒度的加锁机制Segment“分段锁”,JDK8中采用CAS无锁算法。

2.CopyOnWriteArrayList

对应的非并发容器:ArrayList

目标:代替Vector、synchronizedList

原理:利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面修改,然后将新集合赋值给旧的引用,并通过volatile 保证其可见性,当然写操作的锁是必不可少的了。

3.CopyOnWriteArraySet

对应的非并发容器:HashSet

目标:代替synchronizedSet

原理:基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法,其遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。

4.ConcurrentSkipListMap

对应的非并发容器:TreeMap

目标:代替synchronizedSortedMap(TreeMap)

原理:Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。

5.ConcurrentSkipListSet

对应的非并发容器:TreeSet

目标:代替synchronizedSortedSet

原理:内部基于ConcurrentSkipListMap实现

6.ConcurrentLinkedQueue

不会阻塞的队列

对应的非并发容器:Queue

原理:基于链表实现的FIFO队列(LinkedList的并发版本)

7.LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue

对应的非并发容器:BlockingQueue

特点:拓展了Queue,增加了可阻塞的插入和获取等操作

原理:通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒

实现类:

  •  LinkedBlockingQueue:基于链表实现的可阻塞的FIFO队列
  •  ArrayBlockingQueue:基于数组实现的可阻塞的FIFO队列
  •  PriorityBlockingQueue:按优先级排序的队列

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章