编辑
2024-03-17
学习记录
00
请注意,本文编写于 382 天前,最后修改于 17 天前,其中某些信息可能已经过时。

目录

什么是AQS
Semaphore信号量
CountDownLatch

什么是AQS

简单描述一下,aqs就是抽象队列同步器,它是一个抽象类,在java.util.current下,是很多同步器的基础,比如可重入锁、信号量,倒计时,使用整数类型的state变量来表示资源同步状态,这个变量是可见性的,一个线程获取资源时,就会对这个变量的状态进行判断如果是0,表示可以获取到锁,如果大于0就说明资源已经被占用,线程就会进入队列进行等待,队列是一个CLH的链表,将线程的请求封装在node节点中,节点包含前驱、后驱、线程信息、工作状态等信息,如果前面的线程释放资源后会唤醒后面的一个或者多个节点。

Semaphore信号量

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

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 许可协议。转载请注明出处!