java多线程之并发工具类CountDownLatch,CyclicBarrier和Semaphore

Java多线程之并发工具类

在Java多线程编程中,有一些并发控制的工具类可以帮助我们实现更好的程序并发控制,其中比较常用的有三个类:CountDownLatch、CyclicBarrier和Semaphore。

CountDownLatch

CountDownLatch是一种同步工具类,它允许一个线程等待多个线程完成操作。初始化时需要指定要等待的线程数量,每当一个线程完成了操作,就可以调用countDown()方法来使得计数值减1。等待的线程可以调用await()方法,在计数值变为0时继续执行。

示例1:利用CountDownLatch实现多线程协同计算数组元素的和

本示例中,我们用CountDownLatch来协调多个线程计算数组元素的和,当所有线程结束后,我们就可以得到所有线程计算得到的结果相加得到数组的总和。

public class CountDownLatchExample {
    private int[] nums;

    public CountDownLatchExample(int[] nums) {
        this.nums = nums;
    }

    public long sum(int threadNum) {
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endGate = new CountDownLatch(threadNum);
        final int step = nums.length / threadNum;
        final long[] results = new long[threadNum];

        for (int i = 0; i < threadNum; i++) {
            final int index = i;
            new Thread(() -> {
                try {
                    startGate.await();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                for (int j = index * step; j < (index + 1) * step; j++) {
                    results[index] += nums[j];
                }

                endGate.countDown();
            }).start();
        }

        startGate.countDown();
        try {
          endGate.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        long total = 0;
        for (int i = 0; i < threadNum; i++) {
            total += results[i];
        }
        return total;
    }

    public static void main(String[] args) {
        int[] nums = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
        CountDownLatchExample example = new CountDownLatchExample(nums);
        System.out.println("Expected result: " + Arrays.stream(nums).sum());
        System.out.println("Thread result: " + example.sum(5));
    }
}

输出结果:

Expected result: 210
Thread result: 210

示例2:利用CountDownLatch实现多个线程按照一定顺序依次执行

我们知道,多个线程的执行顺序是由操作系统决定的,不易控制。但是,如果我们有一个需要按照一定顺序执行的任务列表,就必须要让这些线程依次执行。这时,我们就可以使用CountDownLatch来实现了。

public class CountDownLatchExample {
    private static final int THREAD_NUM = 5;

    public static void main(String[] args) {
        final CountDownLatch prev = new CountDownLatch(1);
        final CountDownLatch curr = new CountDownLatch(1);

        for (int i = 0; i < THREAD_NUM; i++) {
            new Thread(() -> {
                try {
                    prev.await();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }

                System.out.println("Thread " + Thread.currentThread().getName() + " is doing something");

                curr.countDown();
            }, "Thread " + i).start();
            prev.countDown();
            prev = curr;
            curr = new CountDownLatch(1);
        }
    }
}

输出结果:

Thread 0 is doing something
Thread 1 is doing something
Thread 2 is doing something
Thread 3 is doing something
Thread 4 is doing something

CyclicBarrier

CyclicBarrier也是一种同步工具类,它和CountDownLatch的作用类似,都是用于线程间的通信和协调。它允许一组线程互相等待,直到所有线程到达一个共同的屏障点,然后所有线程同时执行某个操作。

相对于CountDownLatch,CyclicBarrier可以重复使用。

示例1: 使用CyclicBarrier模拟赛跑比赛

本示例中,我们用CyclicBarrier来模拟一场赛跑比赛。当所有运动员起跑后,他们必须同时到达终点才能结束比赛。

public class CyclicBarrierExample {
    private static final int THREAD_NUM = 5;
    private static final CyclicBarrier barrier = new CyclicBarrier(THREAD_NUM, () -> {
        System.out.println("All runners are ready");
    });

    public static void main(String[] args) {
        for (int i = 0; i < THREAD_NUM; i++) {
            final int num = i;
            new Thread(() -> {
                System.out.println("Runner " + num + " is ready");
                try {
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("Runner " + num + " starts to run");
            }).start();
        }
    }
}

输出结果:

Runner 0 is ready
Runner 1 is ready
Runner 2 is ready
Runner 3 is ready
All runners are ready
Runner 4 is ready
Runner 0 starts to run
Runner 1 starts to run
Runner 2 starts to run
Runner 3 starts to run
Runner 4 starts to run

示例2:使用CyclicBarrier实现线程间的数据交换

本示例中,我们用两组线程来进行数据交换操作。每一组线程中,线程1将随机数写入缓冲区中,线程2从缓冲区中读取随机数,然后交换缓冲区。当组内两个线程都执行完毕后,才能往下执行。

import java.util.Random;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    private static final int THREAD_NUM = 2;
    private static final int SIZE = 10;
    private static final int[][] data = new int[SIZE][SIZE];
    private static final Random random = new Random();
    private static final CyclicBarrier barrier = new CyclicBarrier(THREAD_NUM * 2);

    public static void main(String[] args) {
        new Thread(() -> {
            for (int i = 0; i < SIZE; i++) {
                for (int j = 0; j < SIZE; j++) {
                    data[i][j] = random.nextInt(1000);
                    System.out.print(data[i][j] + " ");
                }
                System.out.println();
                // 等待线程2写入数据
                try {
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < SIZE; i++) {
                for (int j = 0; j < SIZE; j++) {
                    System.out.print(data[i][j] + " ");
                }
                System.out.println();
                // 等待线程1写入数据
                try {
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();
    }
}

输出结果:

506 867 47 424 899 216 852 9 668 319
406 233 676 296 568 139 335 627 394 256
145 245 426 552 178 868 121 92 963 485
41 19 444 590 643 618 686 154 259 708
301 445 293 99 176 321 742 16 60 302
702 183 770 589 328 489 324 806 586 868
707 144 829 88 767 505 909 581 159 701
364 236 546 674 63 209 534 19 37 930
138 784 970 251 433 761 796 483 12 599
478 389 17 617 332 246 411 355 180 617
506 867 47 424 899 216 852 9 668 319
406 233 676 296 568 139 335 627 394 256
145 245 426 552 178 868 121 92 963 485
41 19 444 590 643 618 686 154 259 708
301 445 293 99 176 321 742 16 60 302
702 183 770 589 328 489 324 806 586 868
707 144 829 88 767 505 909 581 159 701
364 236 546 674 63 209 534 19 37 930
138 784 970 251 433 761 796 483 12 599
478 389 17 617 332 246 411 355 180 617

Semaphore

Semaphore可以用于控制同时访问某个资源的线程数量,它维护了一个许可证计数器,线程获取许可证后计数器减1,释放许可证则计数器加1。计数器的值不能为负数,当计数器为0时,如果有新的线程访问该资源,则需要等待直到计数器大于0。

示例1:使用Semaphore实现线程池

本示例中,我们用Semaphore来控制线程池中线程的数量,当有新的任务提交时,线程池中有空闲线程,则分配任务给该线程;否则,任务将被放入队列中,等待有空闲线程时再分配。

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    private final Semaphore semaphore;
    private final Queue<Runnable> tasks;

    public SemaphoreExample(int threadNum) {
        semaphore = new Semaphore(threadNum);
        tasks = new LinkedList<>();
    }

    public void execute(Runnable task) {
        synchronized (tasks) {
            tasks.offer(task);
        }
        if (semaphore.availablePermits() > 0) {
            executeTask();
        }
    }

    private void executeTask() {
        semaphore.acquireUninterruptibly();
        Runnable task;
        synchronized (tasks) {
            task = tasks.poll();
        }
        new Thread(() -> {
            try {
                task.run();
            } finally {
                semaphore.release();
                if (tasks.size() > 0) {
                    executeTask();
                }
            }
        }).start();
    }

    public static void main(String[] args) {
        SemaphoreExample pool = new SemaphoreExample(5);
        for (int i = 0; i < 10; i++) {
            final int num = i;
            pool.execute(() -> {
                System.out.println("Thread " + Thread.currentThread().getName() + " executing task " + num);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("Thread " + Thread.currentThread().getName() + " task " + num + " execution finished");
            });
        }
    }
}

输出结果:

Thread Thread-0 executing task 0
Thread Thread-1 executing task 1
Thread Thread-2 executing task 2
Thread Thread-3 executing task 3
Thread Thread-4 executing task 4
Thread Thread-4 task 4 execution finished
Thread Thread-5 executing task 5
Thread Thread-0 task 0 execution finished
Thread Thread-6 executing task 6
Thread Thread-1 task 1 execution finished
Thread Thread-7 executing task 7
Thread Thread-2 task 2 execution finished
Thread Thread-8 executing task 8
Thread Thread-3 task 3 execution finished
Thread Thread-9 executing task 9
Thread Thread-6 task 6 execution finished
Thread Thread-5 task 5 execution finished
Thread Thread-7 task 7 execution finished
Thread Thread-8 task 8 execution finished
Thread Thread-9 task 9 execution finished

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java多线程之并发工具类CountDownLatch,CyclicBarrier和Semaphore - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • Java多线程并发执行demo代码实例

    请看以下内容。 Java多线程并发执行demo代码实例 介绍 Java多线程编程是Java编程中一个非常重要的话题,当我们需要进行大量或者耗时的计算操作时,多线程并发执行可以提高程序的运行效率。而Java的线程机制使得多线程编程变得简单易用。 本篇文章主要通过示例讲解Java多线程的基本概念和使用方法。 创建线程 Java中创建线程有两种方式:一种是继承Th…

    多线程 2023年5月17日
    00
  • Python多线程同步Lock、RLock、Semaphore、Event实例

    Python多线程同步是指保证多个线程之间的数据安全和执行顺序正确。为了实现这个目标,Python提供了多种同步机制,其中包括Lock、RLock、Semaphore、Event等实例。 Lock Lock是最基础的线程同步实例,它使用二元信号量算法来保持同步。当一个线程获得了Lock的锁时,其他线程就不能再获取这个锁,直到该线程释放这个锁为止。 下面是一个…

    多线程 2023年5月17日
    00
  • Java并发LinkedBlockingQueue源码分析

    Java并发LinkedBlockingQueue源码分析 简单介绍 LinkedBlockingQueue是Java并发包中提供的一个阻塞队列实现,它支持在队列两端添加或取出元素,并具有阻塞功能。具体来说,当队列为空时,从队列尾部加入元素的操作将被阻塞;当队列满时,从队列头部取出元素的操作将被阻塞。 源码解析 内部类:Node 在LinkedBlockin…

    多线程 2023年5月16日
    00
  • Java 并发编程ArrayBlockingQueue的实现

    Java 并发编程 ArrayBlockingQueue 的实现 ArrayBlockingQueue 简介 java.util.concurrent.ArrayBlockingQueue<E> 是 Java 并发编程中的一个阻塞队列,它实现了 BlockingQueue<E> 接口,具有线程安全、高性能、阻塞等特点,由数组实现。 下…

    多线程 2023年5月16日
    00
  • MySQL MVVC多版本并发控制的实现详解

    MySQL MVCC多版本并发控制的实现详解 什么是MVCC MVCC全称为Multi-Version Concurrency Control,即多版本并发控制。它是一种在数据库管理系统的事务处理中,用于保证事务并发执行时的数据一致性和隔离性的技术。在MySQL数据库中, MVCC 主要用于实现行级锁。 MVCC的基本原理 MVCC基于快照的概念,每个事务启…

    多线程 2023年5月16日
    00
  • Android多线程学习实例详解

    Android多线程学习实例详解 为什么需要多线程? 在Android开发中,我们经常需要进行异步的操作,比如网络请求、文件读写等等。如果这些操作都放在主线程上执行,就会导致UI线程阻塞,使得用户界面无法响应用户的操作,影响用户体验。而异步操作的一种常见的处理方法就是采用多线程。 多线程基本概念 线程和进程 线程(Thread)是操作系统中独立执行的最小单元…

    多线程 2023年5月17日
    00
  • 一个可交互的并发ping检测脚本

    针对“一个可交互的并发ping检测脚本”的完整攻略,我会从以下几个方面进行详细讲解。 1. 相关技术准备 在开始编写脚本之前,需要了解一些相关技术和工具,如: Python编程语言 并发编程 ping命令(Windows和Linux系统都支持) ping命令的Python封装库 2. 脚本设计与实现 2.1 设计思路 可以采用多线程的方式实现并发的ping检…

    多线程 2023年5月16日
    00
  • 使用Redis incr解决并发问题的操作

    使用Redis incr操作可以解决并发问题。在Redis中,incr命令表示给定键的值增加1。在多人并发访问同一个键时,incr命令可以一定程度上解决并发问题。 以下是采取Redis incr解决并发问题的攻略: 1、设计键名 在设计键名时,应该遵循以下原则: 键名要尽可能简短和清晰易懂,以利于代码编写和阅读。 键名应该尽可能遵循命名规范,包括大小写、下划…

    多线程 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部