Java并发编程代码练习
1. 线程安全的单例模式
单例模式是一种常见的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。在多线程环境下,单例模式的实现需要考虑线程安全问题。以下是使用双重检查锁定(Double-Checked Locking)实现的线程安全单例模式:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
关键点:
volatile
关键字确保instance
变量的可见性,防止指令重排序。- 双重检查锁定(Double-Checked Locking)减少同步开销,只有在第一次创建实例时才进行同步。
2.多线程交替打印数字/字母
线程池提交三个线程,交替打印 1-30
之间的所有数字
代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreeThreadsAlternatePrint {
private static final int MAX_NUMBER = 30; // 打印的最大值
private static final int THREAD_COUNT = 3; // 线程数量
private static int current = 1; // 当前要打印的值
private static final Object lock = new Object(); // 锁对象
static class MyThread implements Runnable {
int id; // 线程ID
public MyThread(int id) {
this.id = id;
}
@Override
public void run() {
while (true) {
synchronized (lock) {
// 检查是否超出最大值
if (current > MAX_NUMBER) {
break;
}
// 判断当前线程是否应该打印
if (current % THREAD_COUNT == id % THREAD_COUNT) {
System.out.println("线程 " + id + " 打印了 " + current++);
lock.notifyAll(); // 唤醒其他线程
} else {
try {
lock.wait(); // 当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
executorService.submit(new MyThread(i + 1));
}
// 等待线程池任务完成
executorService.shutdown();
while (!executorService.isTerminated()) {
// 等待所有任务完成
}
System.out.println("所有线程任务完成!");
}
}
关键点
线程同步基础
利用 synchronized
关键字锁定共享资源至关重要。在这里,创建一个专门的锁对象(如 Object
类型的实例),线程在访问关键共享数据(如当前要打印的数字)前必须获取该锁。这如同多个人要进入一个只能容纳一人的房间拿取物品,只有拿到房间钥匙(锁)的人才能进去,其他人只能等待,有效防止了数据的并发修改冲突,保证了数据一致性。
线程等待与唤醒机制
当线程判断当前并非自己的打印时机时,调用 wait
方法进入等待队列,释放所持有的锁,避免占用资源且让其他线程有机会获取锁推进任务。而一旦某个线程完成打印,通过 notifyAll
唤醒所有等待线程,使它们重新竞争锁,检查是否轮到自己打印,这种巧妙的等待 - 唤醒逻辑让线程间的协作得以流畅进行,就像生产线的工人,完成自己工序后通知下一个工序的工人开工。
循环控制与退出条件
每个线程内部通过一个持续运行的 while
循环来不断检查打印条件,但同时设置了明确的退出条件,即当要打印的数字超过预先设定的最大值时,线程终止循环,避免无意义的资源消耗。这类似长跑比赛,选手们不断向前跑(循环),但跑到终点线(退出条件)就停止。
线程池运用优势
采用线程池(如 Executors.newFixedThreadPool
创建固定大小的线程池)来管理线程任务,具有多重优势。一方面提升了线程创建与销毁的效率,避免频繁创建和销毁线程带来的开销;另一方面,线程池对线程进行统一调度,可确保资源合理分配,使整个多线程系统更加稳定高效,类似一个工厂对工人进行集中调配,哪里有任务就分配工人过去,而不是不断临时招聘和解雇工人。
线程池提交三个线程,交替打印 ABC
十次
代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreeThreadsAlternatePrintABC {
private static final int PRINT_TIMES = 30;
private static final int THREAD_COUNT = 3;
private static int currentCount = 0;
private static final Object lock = new Object();
public static class MyThread implements Runnable {
int id;
char printChar;
public MyThread(int id, char printChar) {
this.id = id;
this.printChar = printChar;
}
@Override
public void run() {
while (true) {
synchronized (lock) {
// 当打印次数达到上限时,退出循环
if (currentCount >= PRINT_TIMES) {
lock.notifyAll();
break;
}
// 判断当前线程是否轮到打印
if (currentCount % THREAD_COUNT == id) {
System.out.print(printChar);
currentCount++;
lock.notifyAll();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
}
}
}
public static void main(String[] args) {
ExecutorService executors = Executors.newFixedThreadPool(3);
char ch = 'A';
for (int i = 0; i < THREAD_COUNT; i++) {
executors.execute(new MyThread(i, ch++));
}
executors.shutdown();
while (!executors.isTerminated()) {
// 等待所有任务完成
}
System.out.println();
System.out.println("所有线程任务完成!");
}
}
使用 ReentrantLock
+ Condition
交替打印 ABC
十次
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreeThreadsAlternatePrintABC {
private static final int PRINT_TIMES = 10;
private static final int THREAD_COUNT = 3;
private static int currentCount = 0; // 当前打印的次数
private static int currentTurn = 0; // 当前轮到的线程编号
private static final Lock lock = new ReentrantLock();
private static final Condition[] conditions = new Condition[THREAD_COUNT];
static {
for (int i = 0; i < THREAD_COUNT; i++) {
conditions[i] = lock.newCondition();
}
}
static class MyThread implements Runnable {
int id;
char printChar;
public MyThread(int id, char printChar) {
this.id = id;
this.printChar = printChar;
}
@Override
public void run() {
while (true) {
lock.lock();
try {
if (currentCount >= PRINT_TIMES) {
// 唤醒所有等待的线程,避免有线程一直等待
for (Condition condition : conditions) {
condition.signal();
}
break;
}
if (currentTurn % THREAD_COUNT != id) {
try {
conditions[id].await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (currentCount < PRINT_TIMES) {
System.out.print(printChar);
currentTurn++;
currentCount++;
// 唤醒下一个线程
conditions[(id + 1) % THREAD_COUNT].signal();
}
} finally {
lock.unlock();
}
}
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
char ch = 'A';
for (int i = 0; i < THREAD_COUNT; i++) {
executorService.execute(new MyThread(i, ch++));
}
executorService.shutdown();
while (!executorService.isTerminated()) {
// 等待所有任务完成
}
System.out.println();
System.out.println("所有线程任务完成!");
}
}
3. 使用 ReentrantLock
+ Condition
+ CountDownLatch
+ ThreadPool
实现生产者消费者问题
生产者消费者问题是多线程编程中的经典问题,涉及生产者和消费者之间的同步。以下是使用 ReentrantLock
、Condition
、CountDownLatch
和线程池实现的生产者消费者模型:
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ProducerConsumerWithThreadPool {
private static final int CAPACITY = 10; // 缓冲区容量
private static final int PRODUCER_COUNT = 3; // 生产者数量
private static final int CONSUMER_COUNT = 5; // 消费者数量
private static final int TOTAL_DATA = 30; // 总共生产的数据量
private static Queue<Integer> queue = new LinkedList<>();
private static Lock lock = new ReentrantLock();
private static Condition notFull = lock.newCondition();
private static Condition notEmpty = lock.newCondition();
private static CountDownLatch producerLatch = new CountDownLatch(PRODUCER_COUNT);
private static CountDownLatch consumerLatch = new CountDownLatch(CONSUMER_COUNT);
private static int producedCount = 0; // 已生产的数据量
private static int consumedCount = 0; // 已消费的数据量
public static void main(String[] args) throws InterruptedException {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(PRODUCER_COUNT + CONSUMER_COUNT);
// 提交生产者任务
for (int i = 0; i < PRODUCER_COUNT; i++) {
executorService.submit(new Producer(i + 1));
}
// 提交消费者任务
for (int i = 0; i < CONSUMER_COUNT; i++) {
executorService.submit(new Consumer(i + 1));
}
// 等待所有生产者线程完成
producerLatch.await();
// 等待所有消费者线程完成
consumerLatch.await();
// 关闭线程池
executorService.shutdown();
System.out.println("测试结束信息");
}
static class Producer implements Runnable {
private int id;
public Producer(int id) {
this.id = id;
}
@Override
public void run() {
try {
while (true) {
lock.lock();
try {
// 如果已经生产了足够的数据,退出
if (producedCount >= TOTAL_DATA) {
break;
}
while (queue.size() == CAPACITY) {
notFull.await(); // 等待缓冲区不满
}
// 生产数据
int data = ++producedCount;
queue.offer(data);
System.out.println("生产者 " + id + " 生产了数据: " + data);
notEmpty.signal(); // 通知消费者可以消费
} finally {
lock.unlock();
}
Thread.sleep(100); // 模拟生产耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
producerLatch.countDown(); // 生产者线程结束
}
}
}
static class Consumer implements Runnable {
private int id;
public Consumer(int id) {
this.id = id;
}
@Override
public void run() {
try {
while (true) {
lock.lock();
try {
// 如果已经消费了所有数据,退出
if (consumedCount >= TOTAL_DATA) {
break;
}
while (queue.isEmpty()) {
if (producedCount >= TOTAL_DATA) {
// 如果生产者已经结束且队列为空,则消费者线程退出
return;
}
notEmpty.await(); // 等待缓冲区不空
}
// 消费数据
int data = queue.poll();
consumedCount++;
System.out.println("消费者 " + id + " 消费了数据: " + data);
notFull.signal(); // 通知生产者可以生产
} finally {
lock.unlock();
}
Thread.sleep(200); // 模拟消费耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
consumerLatch.countDown(); // 消费者线程结束
}
}
}
}
关键点:
ReentrantLock
和Condition
用于线程间的同步。CountDownLatch
用于等待所有生产者和消费者线程完成任务。- 线程池管理生产者和消费者线程。
4. 使用 BlockingQueue
实现生产者消费者问题
BlockingQueue
是 Java 并发包中的一个接口,提供了线程安全的队列操作。以下是使用 BlockingQueue
实现的生产者消费者模型:
package ProducerConsumer;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class test {
private static final int PRODUCER_COUNT = 3; // 生产者数量
private static final int CONSUMER_COUNT = 5; // 消费者数量
private static final int TOTAL_DATA = 30; // 总共生产的数据量
private static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10); // 缓冲区容量为 10
private static CountDownLatch productLatch = new CountDownLatch(PRODUCER_COUNT);
private static CountDownLatch consumeLatch = new CountDownLatch(CONSUMER_COUNT);
private static AtomicInteger producedCount = new AtomicInteger(1); // 已生产的数据量
private static final Integer END_MARKER = -1; // 结束标记
public static void main(String[] args) throws InterruptedException {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(PRODUCER_COUNT + CONSUMER_COUNT);
// 提交生产者任务
for (int i = 0; i < PRODUCER_COUNT; i++) {
executorService.submit(new Producer(i + 1));
}
// 提交消费者任务
for (int i = 0; i < CONSUMER_COUNT; i++) {
executorService.submit(new Consumer(i + 1));
}
// 等待所有生产者线程完成
productLatch.await();
System.out.println("所有生产者已完成生产");
// 向队列中放入结束标记,通知消费者线程退出
for (int i = 0; i < CONSUMER_COUNT; i++) {
queue.put(END_MARKER);
System.out.println("向队列中添加结束标记");
}
// 等待所有消费者线程完成
consumeLatch.await();
System.out.println("所有消费者已完成消费");
// 关闭线程池
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.SECONDS); // 等待线程池中的任务完成
System.out.println("测试结束信息");
}
static class Producer implements Runnable {
private int id;
public Producer(int id) {
this.id = id;
}
@Override
public void run() {
try {
while (producedCount.get() <= TOTAL_DATA) {
int data = producedCount.getAndIncrement();
if (data > TOTAL_DATA) {
producedCount.decrementAndGet(); // 回滚,避免超出范围
break;
}
queue.put(data);
System.out.println("生产者 " + id + " 生产了数据: " + data + ",当前队列大小: " + queue.size());
Thread.sleep(200); // 模拟生产耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("生产者 " + id + " 线程被中断");
} finally {
productLatch.countDown();
}
}
}
static class Consumer implements Runnable {
int id;
public Consumer(int id) {
this.id = id;
}
@Override
public void run() {
try {
while (true) {
int data = queue.take();
if (data == END_MARKER) {
break;
}
System.out.println("消费者 " + id + " 消费了数据: " + data + ",当前队列大小: " + queue.size());
Thread.sleep(300); // 模拟消费耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("消费者 " + id + " 线程被中断");
} finally {
consumeLatch.countDown(); // 消费者线程结束
}
}
}
}
关键点:
BlockingQueue
提供了线程安全的队列操作,简化了同步逻辑。- 使用
END_MARKER
作为结束标记,通知消费者线程退出。 CountDownLatch
用于等待所有生产者和消费者线程完成任务。
总结
本文介绍了线程安全的单例模式以及两种实现生产者消费者问题的方法。第一种方法使用 ReentrantLock
和 Condition
实现,第二种方法使用 BlockingQueue
实现。两种方法各有优缺点,选择哪种方法取决于具体的应用场景和需求。
Leetcode1114题 按序打印 多种实现方案
一、功能陈述
本代码展示了在多线程环境下,确保 Foo
类中的 first
、second
和 third
方法按顺序执行的多种实现方式。具体需求为三个不同的线程分别调用 Foo
类的这三个方法,且必须保证 second
方法在 first
方法之后执行,third
方法在 second
方法之后执行。通过使用 volatile
变量、synchronized
关键字、CountDownLatch
以及 ReentrantLock
结合 Condition
等技术手段,实现了线程间的同步和方法执行顺序的控制,以满足在诸如复杂业务流程处理、资源有序访问等场景下对线程执行顺序的严格要求。
二、关键点
volatile
变量的使用:- 在第一个
Foo
类实现中,通过声明一个volatile
类型的变量count
,利用volatile
的可见性特性,保证了不同线程对count
变量的修改能够及时被其他线程感知。 - 每个方法在执行前检查
count
的值,只有当count
达到相应条件时才执行方法体,从而实现了方法的序列执行。例如,second
方法在count
小于1
时会持续等待,直到first
方法执行使count
自增到1
才继续。 - 然而,需要着重注意的是,
volatile
仅能确保变量的可见性,对于涉及多个操作步骤的复合操作,像count++
这种自增操作,其原子性无法得到保证,在高并发场景下可能出现数据不一致问题。
- 在第一个
synchronized
关键字的运用:- 第二个
Foo
类实现巧妙运用synchronized
关键字针对lock
对象进行同步操作,并与wait
和notifyAll
方法紧密配合。 - 各个方法进入临界区前先获取锁,随即检查状态变量
cnt
,一旦条件不满足,立即调用wait
方法让线程进入等待状态,释放锁资源以避免资源浪费与死锁风险;待条件契合,线程被唤醒后执行方法体并更新cnt
,最后调用notifyAll
唤醒所有因等待该锁而阻塞的线程,重新竞争锁资源。 - 这种经典的锁机制与线程通信模式,不但有力确保了方法按既定顺序执行,而且借助锁的互斥特性,保障了操作的原子性,从根本上杜绝了多线程并发访问共享资源导致的数据混乱,全方位维护线程安全。
- 第二个
CountDownLatch
的应用:- 第三个
Foo
类实现巧妙借助CountDownLatch
类,初始化一个计数初始值为3
的CountDownLatch
对象countDownLatch
。 - 每个方法在圆满执行完毕后,负责调用
countDown
方法将计数值减1
,而其他方法在启动执行前,会严谨检查CountDownLatch
的当前计数是否精准达到预期阈值来判定是否需要持续等待。 - 直至计数值递减为
0
,意味着前置所有方法均已顺利执行完成,此时所有等待的线程得以冲破阻碍继续前行,进而达成方法的顺序控制目标,这种方式逻辑清晰,易于理解与实现。
- 第三个
ReentrantLock
结合Condition
的实现:- 第四个
Foo
类实现灵活运用ReentrantLock
可重入锁以及Condition
条件变量,构建出一套更为精巧灵活的线程同步与通信体系。 - 每个方法在执行前奏阶段,首先获取锁资源,紧接着依据检查状态变量
state
的值来审慎判断当前是否满足执行条件,倘若条件不符,即刻调用condition.await
方法使线程暂停执行并释放锁,平和地进入等待队列;一旦条件成熟,线程被唤醒后迅速执行方法体并更新state
,最后通过调用condition.signalAll
唤醒所有因等待该条件而蛰伏的线程,促使它们重新燃起斗志,争夺锁资源以推进任务。 - 相较于传统的
synchronized
关键字,ReentrantLock
赋予开发者更精细的锁获取与释放控制权,能够依据实际业务场景灵活调整锁策略;而Condition
则提供了较wait
和notifyAll
更为强大、精准的线程等待与唤醒功能,可针对不同条件分支实现高效的线程调度,极大提升多线程程序的性能与响应速度。
- 第四个
代码
# 一个volatile变量
class Foo {
private volatile int count = 0;
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
count++;
}
public void second(Runnable printSecond) throws InterruptedException {
while(count < 1) {}
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
count++;
}
public void third(Runnable printThird) throws InterruptedException {
while(count < 2) {}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
count++;
}
}
# synchronized
class Foo {
private Object lock = new Object();
private int cnt = 0;
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
synchronized (lock) {
while(cnt != 0) {
lock.wait();
}
printFirst.run();
cnt++;
lock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException {
// printSecond.run() outputs "second". Do not change or remove this line.
synchronized (lock) {
while(cnt != 1) {
lock.wait();
}
printSecond.run();
cnt++;
lock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException {
// printThird.run() outputs "third". Do not change or remove this line.
synchronized (lock) {
while(cnt != 2) {
lock.wait();
}
printThird.run();
cnt++;
lock.notifyAll();
}
}
}
# CountDownLatch
class Foo {
private CountDownLatch countDownLatch = new CountDownLatch(3);
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
countDownLatch.countDown();
}
public void second(Runnable printSecond) throws InterruptedException {
// printSecond.run() outputs "second". Do not change or remove this line.
while(countDownLatch.getCount() != 2);
printSecond.run();;
countDownLatch.countDown();
}
public void third(Runnable printThird) throws InterruptedException {
// printThird.run() outputs "third". Do not change or remove this line.
while(countDownLatch.getCount() != 1);
printThird.run();;
countDownLatch.countDown();
}
}
# ReentrantLock + Condition
class Foo {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private int state = 1;
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
lock.lock();
try {
while(state != 1) {
condition.await();
}
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
state++;
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void second(Runnable printSecond) throws InterruptedException {
lock.lock();
try {
while(state != 2) {
condition.await();
}
// printFirst.run() outputs "first". Do not change or remove this line.
printSecond.run();
state++;
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void third(Runnable printThird) throws InterruptedException {
lock.lock();
try {
while(state != 3) {
condition.await();
}
// printFirst.run() outputs "first". Do not change or remove this line.
printThird.run();
state++;
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
public class Main {
public static void main(String[] args) {
Foo foo = new Foo();
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(() -> {
try {
foo.first(() -> System.out.print("first"));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executorService.submit(() -> {
try {
foo.second(() -> System.out.print("second"));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executorService.submit(() -> {
try {
foo.third(() -> System.out.print("third"));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executorService.shutdown();
}
}
Leetcode1115题 交替打印FooBar
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class FooBar {
private int n;
private final ReentrantLock lock = new ReentrantLock();
private final Condition fooCondition = lock.newCondition();
private final Condition barCondition = lock.newCondition();
private boolean fooTurn = true; // 用于标记当前轮到谁执行
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
try {
while (!fooTurn) {
fooCondition.await();
}
printFoo.run();
fooTurn = false;
barCondition.signal();
} finally {
lock.unlock();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
lock.lock();
try {
while (fooTurn) {
barCondition.await();
}
printBar.run();
fooTurn = true;
fooCondition.signal();
} finally {
lock.unlock();
}
}
}
}
Leetcode 打印零与奇偶数
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;
class ZeroEvenOdd {
private int n;
private ReentrantLock lock = new ReentrantLock();
private Condition conditionZero = lock.newCondition();
private Condition conditionEven = lock.newCondition();
private Condition conditionOdd = lock.newCondition();
private AtomicInteger currentNum = new AtomicInteger(1);
private boolean flagZero = true; // 控制 zero 方法的执行
private boolean flagEven = false; // 控制 even 方法的执行
private boolean flagOdd = false; // 控制 odd 方法的执行
public ZeroEvenOdd(int n) {
this.n = n;
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void zero(IntConsumer printNumber) throws InterruptedException {
while (true) {
lock.lock();
try {
// 如果 currentNum 超过 n,直接退出循环
if (currentNum.get() > n) {
break;
}
// 等待 flagZero 为 true
while (!flagZero) {
conditionZero.await();
}
// 再次检查 currentNum 是否超过 n
if (currentNum.get() > n) {
break;
}
printNumber.accept(0); // 输出 0
flagZero = false; // 关闭 zero 的执行权限
if (currentNum.get() % 2 == 0) {
flagEven = true; // 允许 even 执行
conditionEven.signal(); // 唤醒 even 线程
} else {
flagOdd = true; // 允许 odd 执行
conditionOdd.signal(); // 唤醒 odd 线程
}
} finally {
lock.unlock();
}
}
// 强制唤醒所有线程,确保它们能够退出
lock.lock();
try {
flagEven = true;
flagOdd = true;
conditionEven.signalAll();
conditionOdd.signalAll();
} finally {
lock.unlock();
}
}
public void even(IntConsumer printNumber) throws InterruptedException {
while (true) {
lock.lock();
try {
// 如果 currentNum 超过 n,直接退出循环
if (currentNum.get() > n) {
break;
}
// 等待 flagEven 为 true
while (!flagEven) {
conditionEven.await();
}
// 再次检查 currentNum 是否超过 n
if (currentNum.get() > n) {
break;
}
printNumber.accept(currentNum.getAndIncrement()); // 输出偶数
flagEven = false; // 关闭 even 的执行权限
flagZero = true; // 允许 zero 执行
conditionZero.signal(); // 唤醒 zero 线程
} finally {
lock.unlock();
}
}
// 强制唤醒所有线程,确保它们能够退出
lock.lock();
try {
flagZero = true;
flagOdd = true;
conditionZero.signalAll();
conditionOdd.signalAll();
} finally {
lock.unlock();
}
}
public void odd(IntConsumer printNumber) throws InterruptedException {
while (true) {
lock.lock();
try {
// 如果 currentNum 超过 n,直接退出循环
if (currentNum.get() > n) {
break;
}
// 等待 flagOdd 为 true
while (!flagOdd) {
conditionOdd.await();
}
// 再次检查 currentNum 是否超过 n
if (currentNum.get() > n) {
break;
}
printNumber.accept(currentNum.getAndIncrement()); // 输出奇数
flagOdd = false; // 关闭 odd 的执行权限
flagZero = true; // 允许 zero 执行
conditionZero.signal(); // 唤醒 zero 线程
} finally {
lock.unlock();
}
}
// 强制唤醒所有线程,确保它们能够退出
lock.lock();
try {
flagZero = true;
flagEven = true;
conditionZero.signalAll();
conditionEven.signalAll();
} finally {
lock.unlock();
}
}
}
leetcode 1195 交替打印字符串
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;
class FizzBuzz {
private int n;
private int currentNum = 1; // 当前需要处理的数字
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition(); // 用于线程同步
public FizzBuzz(int n) {
this.n = n;
}
// printFizz.run() outputs "fizz".
public void fizz(Runnable printFizz) throws InterruptedException {
while (true) {
lock.lock();
try {
// 如果 currentNum 超过 n,退出循环
if (currentNum > n) {
break;
}
// 如果 currentNum 能被 3 整除但不能被 5 整除,输出 "fizz"
if (currentNum % 3 == 0 && currentNum % 5 != 0) {
printFizz.run();
currentNum++;
condition.signalAll(); // 唤醒其他线程
} else {
condition.await(); // 等待其他线程处理
}
} finally {
lock.unlock();
}
}
}
// printBuzz.run() outputs "buzz".
public void buzz(Runnable printBuzz) throws InterruptedException {
while (true) {
lock.lock();
try {
// 如果 currentNum 超过 n,退出循环
if (currentNum > n) {
break;
}
// 如果 currentNum 能被 5 整除但不能被 3 整除,输出 "buzz"
if (currentNum % 5 == 0 && currentNum % 3 != 0) {
printBuzz.run();
currentNum++;
condition.signalAll(); // 唤醒其他线程
} else {
condition.await(); // 等待其他线程处理
}
} finally {
lock.unlock();
}
}
}
// printFizzBuzz.run() outputs "fizzbuzz".
public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
while (true) {
lock.lock();
try {
// 如果 currentNum 超过 n,退出循环
if (currentNum > n) {
break;
}
// 如果 currentNum 能被 3 和 5 整除,输出 "fizzbuzz"
if (currentNum % 3 == 0 && currentNum % 5 == 0) {
printFizzBuzz.run();
currentNum++;
condition.signalAll(); // 唤醒其他线程
} else {
condition.await(); // 等待其他线程处理
}
} finally {
lock.unlock();
}
}
}
// printNumber.accept(x) outputs "x", where x is an integer.
public void number(IntConsumer printNumber) throws InterruptedException {
while (true) {
lock.lock();
try {
// 如果 currentNum 超过 n,退出循环
if (currentNum > n) {
break;
}
// 如果 currentNum 既不能被 3 整除也不能被 5 整除,输出数字
if (currentNum % 3 != 0 && currentNum % 5 != 0) {
printNumber.accept(currentNum);
currentNum++;
condition.signalAll(); // 唤醒其他线程
} else {
condition.await(); // 等待其他线程处理
}
} finally {
lock.unlock();
}
}
}
}
leetcode 1226 哲学家进食
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class DiningPhilosophers {
private final Lock[] forks = new ReentrantLock[5]; // 5 把叉子
private final Condition[] conditions = new Condition[5]; // 每个哲学家的条件变量
private final Lock diningLock = new ReentrantLock(); // 用于控制哲学家拿叉子的全局锁
public DiningPhilosophers() {
for (int i = 0; i < 5; i++) {
forks[i] = new ReentrantLock();
conditions[i] = forks[i].newCondition();
}
}
// call the run() method of any runnable to execute its code
public void wantsToEat(int philosopher,
Runnable pickLeftFork,
Runnable pickRightFork,
Runnable eat,
Runnable putLeftFork,
Runnable putRightFork) throws InterruptedException {
int leftFork = philosopher; // 左边叉子的编号
int rightFork = (philosopher + 1) % 5; // 右边叉子的编号
diningLock.lock(); // 全局锁,确保同时只有 4 个哲学家尝试拿叉子
try {
forks[leftFork].lock(); // 拿起左边叉子
forks[rightFork].lock(); // 拿起右边叉子
pickLeftFork.run(); // 执行拿起左边叉子的操作
pickRightFork.run(); // 执行拿起右边叉子的操作
eat.run(); // 进餐
putRightFork.run(); // 放下右边叉子
putLeftFork.run(); // 放下左边叉子
forks[rightFork].unlock(); // 释放右边叉子
forks[leftFork].unlock(); // 释放左边叉子
} finally {
diningLock.unlock(); // 释放全局锁
}
}
}