简单描述一下,aqs就是抽象队列同步器,它是一个抽象类,在java.util.current下,是很多同步器的基础,比如可重入锁、信号量,倒计时,使用整数类型的state变量来表示资源同步状态,这个变量是可见性的,一个线程获取资源时,就会对这个变量的状态进行判断如果是0,表示可以获取到锁,如果大于0就说明资源已经被占用,线程就会进入队列进行等待,队列是一个CLH的链表,将线程的请求封装在node节点中,节点包含前驱、后驱、线程信息、工作状态等信息,如果前面的线程释放资源后会唤醒后面的一个或者多个节点。
synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,而Semaphore(信号量)可以用来控制同时访问特定资源的线程数量。
工作流程,初始化初始化型号量为3,每次线程获取先判断是否小于三,小于3就调用方法加一,从而获取资源,大于3就进入队列等待,等到那三个线程释放资源,从而再获取资源。
java
import java.util.concurrent.Semaphore;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SemaphoreExample {
    // 创建一个只能容纳3个线程同时访问的信号量
    private static final Semaphore semaphore = new Semaphore(3);
    public static void main(String[] args) {
        // 使用线程池创建多个线程
        ExecutorService executor = Executors.newFixedThreadPool(6);
        for (int i = 1; i <= 6; i++) {
            executor.submit(new Task(i));
        }
        // 关闭线程池
        executor.shutdown();
    }
    // 定义任务类,每个任务尝试获取信号量并处理一些工作
    static class Task implements Runnable {
        private final int taskId;
        public Task(int id) {
            this.taskId = id;
        }
        @Override
        public void run() {
            try {
                System.out.println("Task " + taskId + " 正在等待访问资源...");
                // 请求进入临界区
                semaphore.acquire();
                System.out.println("Task " + taskId + " 获得访问权限,正在处理资源...");
                // 模拟一些耗时操作
                Thread.sleep((long)(Math.random() * 1000)); // 睡眠0到1000毫秒之间
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 重新设置线程中断状态
                System.out.println("Task " + taskId + " 被中断了.");
            } finally {
                // 释放临界区
                System.out.println("Task " + taskId + " 完成工作并释放资源。");
                semaphore.release();
            }
        }
    }
}
CountDownLatch 允许 count 个线程阻塞在一个地方,直至所有线程的任务都执行完毕。CountDownLatch 是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当 CountDownLatch 使用完毕后,它不能再次被使用。
工作流程就是将state初始化为需要多少个线程获取的数量,调用await阻塞线程,每次线程获取到后执行countDown方法,将state变量减一,如果state为零说明全部线程都获取完毕了,取消阻塞,回到主线程。
java
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个计数器为3的CountDownLatch实例
        final CountDownLatch latch = new CountDownLatch(3);
        // 使用线程池创建多个线程
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 1; i <= 3; i++) {
            final int taskId = i;
            executor.submit(() -> {
                try {
                    System.out.println("任务 " + taskId + " 开始...");
                    // 模拟一些耗时操作
                    Thread.sleep((long)(Math.random() * 2000)); // 睡眠0到2000毫秒之间
                    System.out.println("任务 " + taskId + " 完成!");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 重新设置线程中断状态
                    System.out.println("任务 " + taskId + " 被中断了.");
                } finally {
                    // 每个任务完成后调用countDown方法,减少计数器
                    latch.countDown();
                }
            });
        }
        // 主线程等待所有子线程完成工作
        System.out.println("主线程正在等待所有任务完成...");
        latch.await(); // 这里会阻塞,直到latch的计数器达到零
        System.out.println("所有任务已完成,主线程继续执行...");
        // 关闭线程池
        executor.shutdown();
    }
}
本文作者:Weee
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!