java并发中的同步器

同步器

Java 并发包中的同步器是一些用于协调多个线程执行的工具,用于实现线程之间的同步和互斥操作。这些同步器提供了不同的机制来控制线程的访问和执行顺序,以实现线程安全和并发控制。

1、Semaphore(信号量)

Semaphore 是 Java 并发包中的同步器之一,用于控制对临界区资源的访问数量。它允许多个线程同时访问临界区资源,但限制了同一时间内可以访问资源的线程数量
Semaphore 维护一个许可证计数,线程可以获取和释放这些许可证。当许可证数量为零时,线程需要等待,直到其他线程释放许可证。

Semaphore 基本用法

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3); // 初始化信号量,允许同时访问的线程数量为3

        // 创建多个线程来模拟访问临界区资源
        for (int i = 1; i <= 5; i++) {
            int threadId = i;
            Thread thread = new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可证,如果没有许可证则阻塞
                    System.out.println("Thread " + threadId + " acquired a permit and is accessing the resource.");
                    Thread.sleep(2000); // 模拟访问临界区资源的耗时操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放许可证
                    System.out.println("Thread " + threadId + " released the permit.");
                }
            });
            thread.start();
        }
    }
}

运行结果:

Thread 1 acquired a permit and is accessing the resource.
Thread 3 acquired a permit and is accessing the resource.
Thread 2 acquired a permit and is accessing the resource.
Thread 2 released the permit.
Thread 4 acquired a permit and is accessing the resource.
Thread 5 acquired a permit and is accessing the resource.
Thread 3 released the permit.
Thread 1 released the permit.
Thread 4 released the permit.
Thread 5 released the permit.

在上述示例中,我们创建了一个 Semaphore 实例,并初始化许可证数量为 3。然后创建了多个线程,每个线程在获取许可证后访问临界区资源,模拟耗时操作后释放许可证。由于许可证数量有限,只有一部分线程能够同时访问资源,其他线程需要等待。

Semaphore 适用场景

  1. 有限资源的并发访问,如数据库连接池、线程池等。
  2. 控制对某个资源的同时访问数量,以避免资源竞争和过度消耗。

2、CountDownLatch

CountDownLatch 是 Java 并发包中的同步器之一,用于实现一种等待机制,允许一个或多个线程等待其他线程完成一组操作后再继续执行。

它通过维护一个计数器来实现等待和通知的机制。

在创建 CountDownLatch 时,需要指定初始计数值,每次调用 countDown() 方法会减少计数值,当计数值达到零时,等待的线程会被唤醒继续执行。

CountDownLatch 基本用法

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        int numberOfTasks = 3;
        CountDownLatch latch = new CountDownLatch(numberOfTasks);

        // 创建多个线程来模拟完成任务
        for (int i = 1; i <= numberOfTasks; i++) {
            int taskId = i;
            Thread thread = new Thread(() -> {
                try {
                    System.out.println("Task " + taskId + " is executing...");
                    Thread.sleep(2000); // 模拟任务执行耗时
                    System.out.println("Task " + taskId + " is completed.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown(); // 完成任务后减少计数
                }
            });
            thread.start();
        }

        try {
            System.out.println("Main thread is waiting for tasks to complete...");
            latch.await(); // 等待所有任务完成
            System.out.println("All tasks are completed. Main thread continues.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

Task 1 is executing...
Main thread is waiting for tasks to complete...
Task 2 is executing...
Task 3 is executing...
Task 1 is completed.
Task 2 is completed.
Task 3 is completed.
All tasks are completed. Main thread continues.

在上述示例中,我们创建了一个 CountDownLatch 实例,并初始化计数值为 3。然后创建了多个线程来模拟完成任务,每个线程执行完任务后调用 countDown() 方法减少计数。主线程在执行 latch.await() 时等待计数值为零,等待所有任务完成后继续执行。

使用 CountDownLatch 可以实现多个线程之间的协调,确保某些操作在其他操作完成后再继续执行。

CountDownLatch 适用场景

  1. 主线程等待多个子线程完成任务后再继续执行。
  2. 等待多个线程完成初始化工作后再开始并行操作。

3、CyclicBarrier

CyclicBarrier 是 Java 并发包中的同步器之一,用于实现一组线程在达到一个共同点之前等待彼此,并在达到共同点后继续执行。它可以被重置并重新使用,适用于需要多个线程协同工作的场景。

CyclicBarrier 维护一个计数器和一个栅栏动作(barrier action)。当线程调用 await() 方法时,计数器减少,当计数器达到零时,所有等待的线程会被唤醒并继续执行,同时会执行栅栏动作计数器可以被重置,并且可以设置栅栏动作,在达到共同点后执行。

CyclicBarrier 基本用法

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 reached the barrier!");

        CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, barrierAction);

        // 创建多个线程来模拟并行执行任务
        for (int i = 1; i <= numberOfThreads; i++) {
            int threadId = i;
            Thread thread = new Thread(() -> {
                try {
                    System.out.println("Thread " + threadId + " is performing its task.");
                    Thread.sleep(2000); // 模拟任务执行耗时
                    System.out.println("Thread " + threadId + " has reached the barrier.");
                    barrier.await(); // 等待其他线程达到栅栏点
                    System.out.println("Thread " + threadId + " continues after the barrier.");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
    }
}

运行结果

Thread 1 is performing its task.
Thread 3 is performing its task.
Thread 2 is performing its task.
Thread 2 has reached the barrier.
Thread 3 has reached the barrier.
Thread 1 has reached the barrier.
All threads reached the barrier!
Thread 1 continues after the barrier.
Thread 2 continues after the barrier.
Thread 3 continues after the barrier.

在上述示例中,我们创建了一个 CyclicBarrier 实例,初始化等待的线程数量为 3,并设置了栅栏动作。
然后创建多个线程,每个线程模拟执行任务后等待其他线程达到栅栏点,当所有线程都达到栅栏点时,栅栏动作会被执行。

使用 CyclicBarrier 可以实现多线程协同工作的场景,确保所有线程在某个共同点之前等待彼此,并在达到共同点后继续执行。

CyclicBarrier 计数器重置用法

package com.lf.java.basic.concurrent;

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

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

        for (int round = 1; round <= numberOfRounds; round++) {
            CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, barrierAction);

            System.out.println("Round " + round + ": Starting tasks");

            // 创建多个线程来模拟并行执行任务
            for (int i = 1; i <= numberOfThreads; i++) {
                int threadId = i;
                int finalRound = round;
                Thread thread = new Thread(() -> {
                    try {
                        System.out.println("Thread " + threadId + " is performing its task for Round " + finalRound);
                        Thread.sleep(2000); // 模拟任务执行耗时
                        System.out.println("Thread " + threadId + " has reached the barrier for Round " + finalRound);
                        barrier.await(); // 等待其他线程达到栅栏点
                        System.out.println("Thread " + threadId + " continues after the barrier for Round " + finalRound);
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                });
                thread.start();
            }

            // 等待所有线程完成当前轮次的任务
            try {
                Thread.sleep(3000); // 等待一段时间以观察效果
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("Round " + round + ": All tasks completed\n");

            // 让当前轮次的所有线程都离开栅栏点,以便重新使用
            barrier.reset();
        }
    }
}

运行结果:

Round 1: Starting tasks
Thread 1 is performing its task for Round 1
Thread 2 is performing its task for Round 1
Thread 3 is performing its task for Round 1
Thread 3 has reached the barrier for Round 1
Thread 2 has reached the barrier for Round 1
Thread 1 has reached the barrier for Round 1
All threads reached the barrier!
Thread 2 continues after the barrier for Round 1
Thread 1 continues after the barrier for Round 1
Thread 3 continues after the barrier for Round 1
Round 1: All tasks completed

Round 2: Starting tasks
Thread 1 is performing its task for Round 2
Thread 2 is performing its task for Round 2
Thread 3 is performing its task for Round 2
Thread 3 has reached the barrier for Round 2
Thread 2 has reached the barrier for Round 2
Thread 1 has reached the barrier for Round 2
All threads reached the barrier!
Thread 1 continues after the barrier for Round 2
Thread 3 continues after the barrier for Round 2
Thread 2 continues after the barrier for Round 2
Round 2: All tasks completed

Round 3: Starting tasks
Thread 1 is performing its task for Round 3
Thread 2 is performing its task for Round 3
Thread 3 is performing its task for Round 3
Thread 1 has reached the barrier for Round 3
Thread 2 has reached the barrier for Round 3
Thread 3 has reached the barrier for Round 3
All threads reached the barrier!
Thread 3 continues after the barrier for Round 3
Thread 1 continues after the barrier for Round 3
Thread 2 continues after the barrier for Round 3
Round 3: All tasks completed

在上述示例中,我们模拟了多轮任务协同。每一轮都创建一个新的 CyclicBarrier 实例,用于协调线程的等待和通知。在每一轮的任务完成后,我们使用 barrier.reset() 来重置计数器,以便进行下一轮的任务协同。

运行这个示例可以看到多轮任务协同的效果,每一轮的任务都会等待所有线程完成后再继续,然后重置计数器以准备下一轮。

CyclicBarrier 适用场景

  1. 将多个线程分成阶段进行,每个阶段需要等待其他线程完成后再继续。
  2. 并行计算中的分治操作,等待所有线程完成分治任务后进行合并计算。

4、Phaser

Phaser 是 Java 并发包中的同步器之一,它提供了更灵活的多阶段线程协调机制,适用于需要分阶段进行多个任务的并行执行和协调的场景。Phaser 可以用于更复杂的同步需求,例如循环的多阶段任务协同

Phaser 维护了一个计数器和多个阶段(phase)。在每个阶段,线程可以注册、等待和注销,以及在某个阶段到达时执行特定的操作

Phaser 基本用法

import java.util.concurrent.Phaser;

public class PhaserExample {
    public static void main(String[] args) {
        int numberOfThreads = 3;
        int numberOfPhases = 3;

        Phaser phaser = new Phaser(numberOfThreads) {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("Phase " + phase + " completed.");
                return phase == numberOfPhases - 1 || registeredParties == 0;
            }
        };

        // 创建多个线程来模拟并行执行任务
        for (int i = 0; i < numberOfThreads; i++) {
            int threadId = i;
            Thread thread = new Thread(() -> {
                for (int phase = 0; phase < numberOfPhases; phase++) {
                    System.out.println("Thread " + threadId + " is in Phase " + phase);
                    phaser.arriveAndAwaitAdvance(); // 等待其他线程到达当前阶段
                }
            });
            thread.start();
        }
    }
}

运行结果:

Thread 0 is in Phase 0
Thread 1 is in Phase 0
Thread 2 is in Phase 0
Phase 0 completed.
Thread 2 is in Phase 1
Thread 1 is in Phase 1
Thread 0 is in Phase 1
Phase 1 completed.
Thread 1 is in Phase 2
Thread 2 is in Phase 2
Thread 0 is in Phase 2
Phase 2 completed.

在上述示例中,我们创建了一个 Phaser 实例,设置初始注册线程数量为 3。然后,我们创建多个线程来模拟并行执行任务,每个线程都会在每个阶段调用 phaser.arriveAndAwaitAdvance() 等待其他线程到达当前阶段。当所有线程都到达后,onAdvance() 方法会被调用,用于执行阶段结束后的操作。

Phaser 提供了更灵活的多阶段协同机制,适用于需要多个阶段的任务协同和并行执行的场景。它还支持动态添加或删除等待线程,使其更适用于动态变化的并发需求。

Phaser 适用场景

  1. 需要分阶段执行的任务,每个阶段可以有不同的线程数。
  2. 需要动态添加或删除等待线程的场景。

5、

6、

7、

你可能感兴趣的:(java并发编程,java基础,java,python,开发语言)