Java并发包中那些值得学习的并发工具类(空谈误国,实干兴邦,代码示范,抛砖引玉)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wd2014610/article/details/85334597

首先我们通常说的并发包就是java.util.concurrent包及其子包。集中了Java并发的各种基础工具类。

一、这个并发包在哪

在这里插入图片描述

上面的包就是传说中的并发包。
为什么这个并发包就比较流弊呢?
原因主要有以下几点。

  1. 提供了几个比synchronized更加高级的各种同步结构。例如:CountDownLatch、CyclicBarrier、Semaphore等。可以实现更加丰富的多线程的操作。比如利用Semaphore作为资源控制器(其实可以简单理解为信号量),限制同时工作的线程的数量。
    在这里插入图片描述

  2. 各种线程安全的容器。例如:无序的ConcurrentHashMap、有序的ConcurrentSkipListMap和线程安全的动态数组CopyOnWriteArrayList等。

在这里插入图片描述

  1. 各种并发队列的实现。比较典型的有ArrayBlockingQueue、SynchorousQueue或者优先级队列PriorityBlockingQueue等。

  2. 强大的Executor框架,可以创建各种不同类型的线程池,调度任务的运行,绝大部分情况下,用户不需要自己从头实现线程池和调度任务。

在这里插入图片描述

二、讲几个比较流弊用的较多的

万变不离其宗,抛砖引玉,如果这几个基础的你了解的比较好,以后再深入拓展就比较简单了。

空谈误国,实干兴邦,谢谢看嘛。

扫描二维码关注公众号,回复: 5776489 查看本文章

1、Semaphore

简单说,他就是一个计数器,其基本逻辑基于acquire/release申请或者获得资源。

  • Semaphore:Java版本的信号量的实现。用这个你就能实现对资源的管理。例如你一辆车做五个人,那么就赋值5。你只有一台打印机可以用,那就是1。

  • 1

package com.newframe.controllers.api;

import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.util.concurrent.Semaphore;

/**
 * 第一步
 * 创建一个实现了Runnable接口的信号量工作类
 * 并重写它的run方法,让他做我想要做的事情
 */

public class SemaphoreWorker implements Runnable{

    private String name;

    private Semaphore semaphore;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //创建一个构造函数
    public SemaphoreWorker(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    @Override
    public void run() {

        try {
            log("准备申请一个资源");
            semaphore.acquire();//申请资源
            log("申请到了资源");
            log("执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            semaphore.release();
            log("释放资源成功");
        }

    }

    private void log(String msg){
        if (StringUtils.isEmpty(msg)){
            name = Thread.currentThread().getName();
        }

        System.out.println(name + " " + msg);
    }
}

  • 2
package com.newframe.controllers.api;

import java.util.concurrent.Semaphore;

/**
 * 第二步
 * 创建一个Semaphore的测试类
 */
public class TestSemaphore {

    public static void main(String[] args) {

        System.out.println("我要开始执行了");

        //创建一个Semaphore对象,允许3个资源
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 10; i++) {
            SemaphoreWorker worker = new SemaphoreWorker(semaphore);
            //给线程一个名字
            worker.setName("线程"+i);
            Thread thread = new Thread(worker);
            thread.start();
        }
    }
}

在上面可以很清晰的看到Semaphore对资源信号量的控制。
如我上面的信号量给的3,就是最多只能有3个线程能同时申请到资源。其余线程需要等待申请到的线程释放资源后才能获得资源。
看输出结果:

在这里插入图片描述

2、CountDownLatch和CyclicBarrier

他们的行为有一定的相似度。经常会用来要你理解他们有什么区别。

  • CountDownLatch 控制的这个数量是不可以重置的,两个线程不能够同时抢占CountDownLatch控制的资源数量。在前面的线程执行完后,后面的线程才可以执行。
  • CycliBarrier这个就很有意思了。这个比如说你有5个士力架,你上学,你吃完了,你一定要把5个都吃完,你立马可以获得新的5个士力架。你怕不怕。

3、CountDownLatch

下面我们来举个例子

  • 1
package com.newframe.controllers.api;

import java.util.concurrent.CountDownLatch;

/**
 * 创建第一个吃巧克力的家伙,叫他小明吧
 * 实现Runnable接口,并重写其方法
 */
public class FirstBatchWorker implements Runnable{

    private CountDownLatch latch;

    public FirstBatchWorker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        //执行countDown()操作
        latch.countDown();
        System.out.println("小明吃了一块巧克力");
    }
}
  • 2
package com.newframe.controllers.api;

import java.util.concurrent.CountDownLatch;

/**
 * 创建第二个吃巧克力的家伙,叫他小李吧
 * 实现Runnable接口,并重写其方法
 */
public class SecondBatchWorker implements Runnable{

    private CountDownLatch latch;

    public SecondBatchWorker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {

        try {
            latch.await();
            System.out.println("小李吃了一块巧克力");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  • 3
package com.newframe.controllers.api;

import java.util.concurrent.CountDownLatch;

/**
 * 来测试一下LatchSample
 */
public class TestCountDownLatch {

    public static void main(String[] args) {

        //一共5个巧克力
        //同时有5个线程可以获取到资源
        CountDownLatch latch = new CountDownLatch(5);

		//下面的线程是按照顺序执行的。
        //五个小明线程
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new FirstBatchWorker(latch));
            thread.start();
        }

        //这个线程一定要等到上个线程全部执行完成了才可以执行
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new SecondBatchWorker(latch));
            thread.start();
        }
    }
}

执行结果

小明吃了一块巧克力
小明吃了一块巧克力
小明吃了一块巧克力
小明吃了一块巧克力
小明吃了一块巧克力
小明吃了一块巧克力
小明吃了一块巧克力
小明吃了一块巧克力
小明吃了一块巧克力
小明吃了一块巧克力
小李吃了一块巧克力
小李吃了一块巧克力
小李吃了一块巧克力
小李吃了一块巧克力
小李吃了一块巧克力

CountDownLatch用于线程间等待操作结束是非常普遍简单的用法。

4、CyclicBarrier

如果说CountDownLatch 是用于线程间等待操作结束的协调,那么CyclicBarrier其实反映的是线程间并行运行时的协调。
下面来看一个例子。

  • 1、
package com.newframe.controllers.api;


import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * 创建一个CyclicBarrier类,实现Runable接口
 */
public class CyclicBarrierWorker implements Runnable{

    private CyclicBarrier cyclicBarrier;

    public CyclicBarrierWorker(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
    }

    @Override
    public void run() {

        try {
            for (int i = 0; i < 3; i++) {
                System.out.println("执行");
                cyclicBarrier.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

  • 2、
package com.newframe.controllers.api;

import java.util.concurrent.CyclicBarrier;

/**
 * 测试一下CyclicBarrier
 */
public class TestCyclicBarrier {

    public static void main(String[] args) {

        CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("操作再次......执行");
            }
        });

        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new CyclicBarrierWorker(barrier));
            thread.start();
        }
    }
}

  • 执行结果
执行
执行
执行
执行
执行
操作再次......执行
执行
执行
执行
执行
执行
操作再次......执行
执行
执行
执行
执行
执行
操作再次......执行

三、Java并发库的小知识

Java并发库还提供了Phaser,功能与CountDownLatch很接近。
Java并发库还提供了线程安全的Map、List和Set等容器。

在这里插入图片描述

ConcurrentHashMap:侧重通过键值对,放入或者获取的速度,对顺序无所谓。
ConcurrentSkipListMap:通过键值对操作数据,侧重数据的顺序操作。如果对大量数据进行频繁的修改,ConcurrentSkipListMap也是有优势的。

好啦,今天就到这里啦。好记性比不上烂笔头,还是的亲自实践一下的。

猜你喜欢

转载自blog.csdn.net/wd2014610/article/details/85334597