简单描述一下,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 许可协议。转载请注明出处!