2023年Java核心技术第十一篇(篇篇万字精讲)

目录

[二十一 . Java并发包提供了哪些并发工具类](#二十一 . Java并发包提供了哪些并发工具类)

[21.1 典型回答](#21.1 典型回答)

[21.1 .1 CountDownLatch](#21.1 .1 CountDownLatch)

[21.1 .2 CyclicBarrier](#21.1 .2 CyclicBarrier)

[21.1 .2.1 例子:](#21.1 .2.1 例子:)

[21.1 .2.2 输出结果:](#21.1 .2.2 输出结果:)

[21.1.2.3 详细解释:屏障点](#21.1.2.3 详细解释:屏障点)

[21.1.3 Semaphore](#21.1.3 Semaphore)

[21.1.3.1 使用 Semaphore例子:](#21.1.3.1 使用 Semaphore例子:)

[21.1.3.2 输出结果:](#21.1.3.2 输出结果:)

[21.1.3.3 安全容器和队列](#21.1.3.3 安全容器和队列)

[21.1.3.4 Executor框架](#21.1.3.4 Executor框架)


二十一 . Java并发包提供了哪些并发工具类

在前面的篇幅中我们进行了,线程,锁,等并发编程的基础元素的讲解。

21.1 典型回答

我们通常所说的并发包也就是 java.util.concurrent 及其子包,集中了 Java 并发的各种基础

工具类。

具体主要包括几个方面:

提供了比 synchronized 更加高级的各种同步结构,包括 CountDownLatch、

CyclicBarrier、Semaphore 等,可以实现更加丰富的多线程操作,比如利用Semap作为资源控制器,限制同时进行工作的线程数量。

21.1 .1 CountDownLatch

创建一个CountDownLatch对象,并将其计数器初始化为3:

java 复制代码
CountDownLatch countDownLatch = new CountDownLatch(3);

创建一个InterfaceRequestThread类,继承自Thread类,用于发起接口请求并倒计时:

java 复制代码
public class InterfaceRequestThread extends Thread {
    private final CountDownLatch countDownLatch;

    public InterfaceRequestThread(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        // 发起接口请求操作
        System.out.println("请求接口数据...");
        try {
            Thread.sleep(2000); // 模拟接口请求耗时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("接口数据请求完成。");

        countDownLatch.countDown(); // 计数器减一
    }
}

主线程中创建多个InterfaceRequestThread线程,并启动它们:

java 复制代码
// 创建CountDownLatch对象
CountDownLatch countDownLatch = new CountDownLatch(3);

// 创建三个InterfaceRequestThread线程,并启动它们
for (int i = 0; i < 3; i++) {
    Thread thread = new InterfaceRequestThread(countDownLatch);
    thread.start();
}

在主线程中调用await()方法等待计数器达到零,然后进行下一步处理:

java 复制代码
try {
    countDownLatch.await(); // 等待计数器达到零
    System.out.println("所有接口数据请求完成,开始进行下一步处理...");
    // 进行下一步操作,例如数据处理等
} catch (InterruptedException e) {
    e.printStackTrace();
}

运行时,会观察到每个InterfaceRequestThread线程都会发起接口请求,并在请求完成后将CountDownLatch的计数器减一。当所有线程的请求都完成后,主线程就会从await()方法中返回,并输出"所有接口数据请求完成,开始进行下一步处理...",表示可以开始下一步操作了。

通过CountDownLatch,我们实现了多个线程之间的协作与同步,确保所有接口数据都请求完毕后再进行下一步操作。这在需要等待其他线程完成某项操作后再继续执行的场景中非常有用。

21.1 .2 CyclicBarrier

CyclicBarrier 是 Java 提供的一种同步工具类,它可以用于多个线程之间相互等待,直到所有线程都达到一个共同的屏障点,然后同时继续执行。

CyclicBarrier 的主要特点如下:

  • 它可以接收一个整数作为构造函数参数,该整数表示需要等待的线程数量。
  • 每个线程在达到屏障点时会调用 await() 方法,此时该线程会被阻塞,直到所有线程都调用了 await() 方法。
  • 当所有线程都调用了 await() 方法后,所有线程会同时被释放,可以继续执行后续操作。
  • CyclicBarrier 的计数器可以重置,因此它可以被重复使用。

21.1 .2.1 例子:

java 复制代码
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int numberOfThreads = 3;
        Runnable barrierAction = () -> System.out.println("All threads have reached the barrier");

        CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, barrierAction);

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

    static class Worker implements Runnable {
        private final CyclicBarrier barrier;

        Worker(CyclicBarrier barrier) {
            this.barrier = barrier;
        }

        @Override
        public void run() {
            try {
                System.out.println("Thread started");
                // 模拟线程的工作
                Thread.sleep(1000);
                System.out.println("Thread finished work and waiting at the barrier");
                barrier.await();
                System.out.println("Thread released from the barrier and continuing its work");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

21.1 .2.2 输出结果:

java 复制代码
Thread started
Thread finished work and waiting at the barrier
Thread started
Thread finished work and waiting at the barrier
Thread started
Thread finished work and waiting at the barrier
All threads have reached the barrier
Thread released from the barrier and continuing its work
Thread released from the barrier and continuing its work
Thread released from the barrier and continuing its work

我们创建了一个 CyclicBarrier 对象,并指定等待的线程数量为 3,同时传入一个 barrierAction,当所有线程都达到屏障点时,会执行该 barrierAction

每个线程在 run() 方法中模拟一些工作,并调用 await() 方法等待其他线程。当所有线程都调用了 await() 方法后,它们会同时被释放,继续执行后续操作。

在输出结果中,可以看到三个线程同时执行到了屏障点,并输出了 "All threads have reached the barrier" 的信息,然后它们继续执行剩余的工作。

通过使用 CyclicBarrier,可以实现多个线程之间的同步,让它们在某个点上进行协调,等待彼此达到同一个状态,然后再继续执行后续操作。这在需要线程间相互等待的场景下非常有用,例如并行计算中将计算结果合并,多个线程同时开始某个任务等等。

21.1.2.3 详细解释:屏障点

屏障点是CyclicBarrier对象的构造函数中指定的等待线程数量达到了设定值。也就是说,当3个线程都调用了await()方法后,它们会在CyclicBarrier对象的屏障点处等待。

具体来说,在该例子中,CyclicBarrier对象的构造函数参数为3,表示需要等待3个线程。当第一个线程调用await()方法时,它会被阻塞,直到所有3个线程都调用了await()方法。当第二个和第三个线程都调用了await()方法后,所有线程都达到了屏障点并等待。

一旦所有线程都达到屏障点,屏障点就会打开,所有线程都会被释放并继续执行后续操作。

21.1.3 Semaphore

Semaphore(信号量)是一种并发控制机制,用于限制同时访问某个共享资源的线程数量。它可以用于控制对临界区的访问,或者限制对有限资源的并发访问。

Semaphore 主要有两个操作:acquire()release()

  • acquire(): 当一个线程需要访问被 Semaphore 保护的资源时,它调用 acquire() 方法进行获取信号量。如果信号量计数器大于0,该线程可以继续执行,同时信号量计数器减1。如果信号量计数器为0,那么该线程将被阻塞,直到有其他线程调用 release() 方法释放信号量。
  • release(): 当一个线程访问完被 Semaphore 保护的资源后,它调用 release() 方法释放信号量。该操作会使信号量计数器增加1,并且唤醒一个或多个被阻塞的线程,允许它们继续执行。

Semaphore 的计数器可以初始化为一个非负整数。如果计数器初始化为1,那么 Semaphore 就变成了互斥锁的功能,也被称为二元信号量。

21.1.3.1 使用 Semaphore例子:

java 复制代码
import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        int numberOfPermits = 3; // 允许同时访问资源的线程数量
        Semaphore semaphore = new Semaphore(numberOfPermits);

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

    static class Worker implements Runnable {
        private final Semaphore semaphore;

        Worker(Semaphore semaphore) {
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                System.out.println("Thread acquiring semaphore");
                semaphore.acquire();
                System.out.println("Thread acquired semaphore and performing its task");
                Thread.sleep(2000);
                System.out.println("Thread released semaphore");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

21.1.3.2 输出结果:

java 复制代码
Thread acquiring semaphore
Thread acquiring semaphore
Thread acquiring semaphore
Thread acquired semaphore and performing its task
Thread acquired semaphore and performing its task
Thread acquired semaphore and performing its task
Thread releasing semaphore
Thread releasing semaphore
Thread releasing semaphore

我们创建了一个 Semaphore 对象,并指定允许同时访问资源的线程数量为3。然后,我们创建了5个线程,并在每个线程中进行一些任务。

每个线程在运行时首先调用 acquire() 方法来获取信号量。如果有可用的许可证(计数器大于0),线程将继续执行,并且信号量计数器减1。如果没有可用的许可证(计数器为0),线程将被阻塞,直到有其他线程释放信号量。

在线程执行完任务后,它调用 release() 方法来释放信号量,使得其他等待的线程能够继续执行。

需要注意的是,在获取和释放信号量时要确保正确使用,以免导致死锁或访问冲突的问题。因此,在编写使用 Semaphore 的程序时,需要仔细设计和管理信号量的获取和释放操作,以确保线程间的正确协调和资源的正确使用。

21.1.3.3 安全容器和队列

各种线程安全的容器,比如常见的ConcurrentHashMap,有序的ConcurrentSkipListMap,或者通过类似快照机制,实现线程安全的动态数组CopyOnWriteArrayList。

以及并发队列实现,如:BlockingQueue实现,比较典型的ArrayBlockingQueue,SynchronousQueue或对特定场景的PriorityBlockingQueue等这些在前面的篇幅中有详细解释。

21.1.3.4 Executor框架

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

相关推荐
你知道“铁甲小宝”吗丶4 分钟前
【第33章】Spring Cloud之SkyWalking服务链路追踪
java·spring boot·spring·spring cloud·skywalking
虚假程序设计7 分钟前
pythonnet python图像 C# .NET图像 互转
开发语言·人工智能·python·opencv·c#·.net
归寻太乙12 分钟前
C++函数重载完成日期类相关计算
开发语言·c++
尽蝶叙15 分钟前
C++:分苹果【排列组合】
开发语言·c++·算法
coffee_baby16 分钟前
化繁为简:中介者模式如何管理复杂对象交互
java·spring boot·microsoft·交互·中介者模式
ღ᭄ꦿ࿐Never say never꧂20 分钟前
微服务架构中的负载均衡与服务注册中心(Nacos)
java·spring boot·后端·spring cloud·微服务·架构·负载均衡
所待.38321 分钟前
小小扑克牌算法
java·算法
.生产的驴29 分钟前
SpringBoot 消息队列RabbitMQ 消息确认机制确保消息发送成功和失败 生产者确认
java·javascript·spring boot·后端·rabbitmq·负载均衡·java-rabbitmq
.生产的驴29 分钟前
SpringBoot 消息队列RabbitMQ在代码中声明 交换机 与 队列使用注解创建
java·spring boot·分布式·servlet·kafka·rabbitmq·java-rabbitmq
憧憬成为原神糕手40 分钟前
c++_list
开发语言·c++