Java并发编程面试代码练习

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; // 线程IDpublic MyThread(int id) {this.id = id;}@Overridepublic 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 创建固定大小的线程池)来管理线程任务,具有多重优势。一方面提升了线程创建与销毁的效率,避免频繁创建和销毁线程带来的开销;另一方面,线程池对线程进行统一调度,可确保资源合理分配,使整个多线程系统更加稳定高效,类似一个工厂对工人进行集中调配,哪里有任务就分配工人过去,而不是不断临时招聘和解雇工人。


3. 使用 ReentrantLock + Condition + CountDownLatch + ThreadPool 实现生产者消费者问题

生产者消费者问题是多线程编程中的经典问题,涉及生产者和消费者之间的同步。以下是使用 ReentrantLockConditionCountDownLatch 和线程池实现的生产者消费者模型:

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;}@Overridepublic 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;}@Overridepublic 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(); // 消费者线程结束}}}
}

关键点:

  • ReentrantLockCondition 用于线程间的同步。
  • 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); // 缓冲区容量为 10private 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;}@Overridepublic 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;}@Overridepublic 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 用于等待所有生产者和消费者线程完成任务。

总结

本文介绍了线程安全的单例模式以及两种实现生产者消费者问题的方法。第一种方法使用 ReentrantLockCondition 实现,第二种方法使用 BlockingQueue 实现。两种方法各有优缺点,选择哪种方法取决于具体的应用场景和需求。


Leetcode1114题 按序打印 多种实现方案

一、功能陈述

本代码展示了在多线程环境下,确保 Foo 类中的 firstsecondthird 方法按顺序执行的多种实现方式。具体需求为三个不同的线程分别调用 Foo 类的这三个方法,且必须保证 second 方法在 first 方法之后执行,third 方法在 second 方法之后执行。通过使用 volatile 变量、synchronized 关键字、CountDownLatch 以及 ReentrantLock 结合 Condition 等技术手段,实现了线程间的同步和方法执行顺序的控制,以满足在诸如复杂业务流程处理、资源有序访问等场景下对线程执行顺序的严格要求。

二、关键点

  1. volatile 变量的使用
    • 在第一个 Foo 类实现中,通过声明一个 volatile 类型的变量 count,利用 volatile 的可见性特性,保证了不同线程对 count 变量的修改能够及时被其他线程感知。
    • 每个方法在执行前检查 count 的值,只有当 count 达到相应条件时才执行方法体,从而实现了方法的序列执行。例如,second 方法在 count 小于 1 时会持续等待,直到 first 方法执行使 count 自增到 1 才继续。
    • 然而,需要着重注意的是,volatile 仅能确保变量的可见性,对于涉及多个操作步骤的复合操作,像 count++ 这种自增操作,其原子性无法得到保证,在高并发场景下可能出现数据不一致问题。
  2. synchronized 关键字的运用
    • 第二个 Foo 类实现巧妙运用 synchronized 关键字针对 lock 对象进行同步操作,并与 waitnotifyAll 方法紧密配合。
    • 各个方法进入临界区前先获取锁,随即检查状态变量 cnt,一旦条件不满足,立即调用 wait 方法让线程进入等待状态,释放锁资源以避免资源浪费与死锁风险;待条件契合,线程被唤醒后执行方法体并更新 cnt,最后调用 notifyAll 唤醒所有因等待该锁而阻塞的线程,重新竞争锁资源。
    • 这种经典的锁机制与线程通信模式,不但有力确保了方法按既定顺序执行,而且借助锁的互斥特性,保障了操作的原子性,从根本上杜绝了多线程并发访问共享资源导致的数据混乱,全方位维护线程安全。
  3. CountDownLatch 的应用
    • 第三个 Foo 类实现巧妙借助 CountDownLatch 类,初始化一个计数初始值为 3CountDownLatch 对象 countDownLatch
    • 每个方法在圆满执行完毕后,负责调用 countDown 方法将计数值减 1,而其他方法在启动执行前,会严谨检查 CountDownLatch 的当前计数是否精准达到预期阈值来判定是否需要持续等待。
    • 直至计数值递减为 0,意味着前置所有方法均已顺利执行完成,此时所有等待的线程得以冲破阻碍继续前行,进而达成方法的顺序控制目标,这种方式逻辑清晰,易于理解与实现。
  4. ReentrantLock 结合 Condition 的实现
    • 第四个 Foo 类实现灵活运用 ReentrantLock 可重入锁以及 Condition 条件变量,构建出一套更为精巧灵活的线程同步与通信体系。
    • 每个方法在执行前奏阶段,首先获取锁资源,紧接着依据检查状态变量 state 的值来审慎判断当前是否满足执行条件,倘若条件不符,即刻调用 condition.await 方法使线程暂停执行并释放锁,平和地进入等待队列;一旦条件成熟,线程被唤醒后迅速执行方法体并更新 state,最后通过调用 condition.signalAll 唤醒所有因等待该条件而蛰伏的线程,促使它们重新燃起斗志,争夺锁资源以推进任务。
    • 相较于传统的 synchronized 关键字,ReentrantLock 赋予开发者更精细的锁获取与释放控制权,能够依据实际业务场景灵活调整锁策略;而 Condition 则提供了较 waitnotifyAll 更为强大、精准的线程等待与唤醒功能,可针对不同条件分支实现高效的线程调度,极大提升多线程程序的性能与响应速度。

代码

# 一个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 为 truewhile (!flagZero) {conditionZero.await();}// 再次检查 currentNum 是否超过 nif (currentNum.get() > n) {break;}printNumber.accept(0); // 输出 0flagZero = 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 为 truewhile (!flagEven) {conditionEven.await();}// 再次检查 currentNum 是否超过 nif (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 为 truewhile (!flagOdd) {conditionOdd.await();}// 再次检查 currentNum 是否超过 nif (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 codepublic 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(); // 释放全局锁}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/37094.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑节电模式怎么退出 分享5种解决方法

在使用电脑的过程中&#xff0c;许多用户为了节省电力&#xff0c;通常会选择开启电脑的节能模式。然而&#xff0c;在需要更高性能或进行图形密集型任务时&#xff0c;节能模式可能会限制系统的性能表现。这时&#xff0c;了解如何正确地关闭或调整节能设置就显得尤为重要了。…

AI学习——卷积神经网络(CNN)入门

作为人类&#xff0c;我们天生擅长“看”东西&#xff1a;一眼就能认出猫狗、分辨红绿灯、读懂朋友的表情……但计算机的“眼睛”最初是一片空白。直到卷积神经网络&#xff08;CNN&#xff09;​的出现&#xff0c;计算机才真正开始理解图像。今天&#xff0c;我们就用最通俗的…

2025年渗透测试面试题总结- shopee-安全工程师(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 shopee-安全工程师 信息安全相关Response头详解 1. 关键安全头及防御场景 Linux与Docker核心命令速查…

IntelliJ IDEA 中 Maven 的 `pom.xml` 变灰带横线?一文详解解决方法

前言 在使用 IntelliJ IDEA 进行 Java 开发时&#xff0c;如果你发现项目的 pom.xml 文件突然变成灰色并带有删除线&#xff0c;这可能是 Maven 的配置或项目结构出现了问题。 一、问题现象与原因分析 现象描述 文件变灰&#xff1a;pom.xml 在项目资源管理器中显示为灰色。…

Spring MVC 接口数据

访问路径设置 RequestMapping("springmvc/hello") 就是用来向handlerMapping中注册的方法注解! 秘书中设置路径和方法的对应关系&#xff0c;即RequestMapping("/springmvc/hello")&#xff0c;设置的是对外的访问地址&#xff0c; 路径设置 精准路径匹…

技术分享 | MySQL内存使用率高问题排查

本文为墨天轮数据库管理服务团队第51期技术分享&#xff0c;内容原创&#xff0c;如需转载请联系小墨&#xff08;VX&#xff1a;modb666&#xff09;并注明来源。 一、问题现象 问题实例mysql进程实际内存使用率过高 二、问题排查 2.1 参数检查 mysql版本 &#xff1a;8.0.…

【redis】什么是持久化之 RDB

什么是持久化 MySQL 的事务&#xff0c;有四个比较核心的特性&#xff1a; 原子性一致性持久性>持久化&#xff08;说的一回事&#xff09; 把数据存储在硬盘上>持久把数据存在内存上>不持久重启进程/重启主机之后&#xff0c;数据是否还存在 隔离性 Redis 是一个内存…

Python、MATLAB和PPT完成数学建模竞赛中的地图绘制

参加数学建模比赛时&#xff0c;很多题目——诸如统计类、数据挖掘类、环保类、建议类的题目总会涉及到地理相关的情景&#xff0c;往往要求我们制作与地图相关的可视化内容。如下图&#xff0c;这是21年亚太赛的那道塞罕坝的题目&#xff0c;期间涉及到温度、降水和森林覆盖率…

Python(冒泡排序、选择排序、插入法排序、快速排序,算法稳定性)

算法的稳定性 冒泡排序 # 冒泡排序 # 1 思想: 相邻位置两个元素比较, 前面的元素比后面的元素大则交换, 把最大的数给找到 # 经过一轮一轮的比较最终把序列给排序 # 2 关键点1: 两层for循环 外层循环控制多少轮 内层for循环控制比较次数 # 3 关键点2: 若遍历一遍没有数字…

【自用】NLP算法面经(5)

一、L1、L2正则化 正则化是机器学习中用于防止过拟合并提高模型泛化能力的技术。当模型过拟合时&#xff0c;它已经很好地学习了训练数据&#xff0c;甚至是训练数据中的噪声&#xff0c;所以可能无法在新的、未见过的数据上表现良好。 比如&#xff1a; 其中&#xff0c;x1和…

PyCharm安装redis,python安装redis,PyCharm使用失败问题

报错信息 Usage: D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip install [options] [package-index-options] … D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip install [options] -r [package-index-options] … D:\wb2\wbrj_pys\venv\Scripts\python.exe -m pip instal…

学习笔记|arduino uno r3|DS1307时钟芯片|Atmega328P| 设置时间|读取时间|无源晶振:DS1307时钟芯片实验

目录 芯片pinout&#xff1a; 实验器件&#xff1a; 实验连线 解决AVR 架构不支持 printf() 方法 使用GetTimeAndDate.ino设置时间&#xff1a; 使用SetTimeAndDate.ino设置时间&#xff1a; 芯片pinout&#xff1a; DS1307 是美国 DALLAS 公司推出的 I 总线接口实时时钟芯…

uniapp可拖拽消息数徽标draggable-badge,仿手机qq聊天列表未读数徽标动效

组件下载地址&#xff1a;https://ext.dcloud.net.cn/plugin?id22679 兼容性&#xff1a; 测试了h5和微信小程序&#xff0c;理论支持全平台&#xff0c;暂不支持pc端&#xff0c;不过可以自己修改事件兼容pc 使用uniapp仿写了一个手机qq聊天列表右侧未读数的徽标组件&#x…

【设计模式】策略模式

以下是格式优化后的Markdown文档&#xff0c;仅调整代码缩进&#xff0c;保持内容不变&#xff1a; 四、策略模式 策略(Strategy) 模式是一种行为型模式&#xff0c;其实现过程与模板方法模式非常类似——都 是以扩展的方式支持未来的变化。本章通过对一个具体范例的逐步重构…

STM32配套程序接线图

1 工程模板 2 LED闪烁 3LED流水灯 4蜂鸣器 5按键控制LED 6光敏传感器控制蜂鸣器 7OLED显示屏 8对射式红外传感器计次 9旋转编码器计次 10 定时器定时中断 11定时器外部时钟 12PWM驱动LED呼吸灯 13 PWM驱动舵机 14 PWM驱动直流电机 15输入捕获模式测频率 16PWMI模式测频率占空…

【C语言】使用结构体实现位段

一、位段 前面我们学习了结构体&#xff0c;位段的声明和结构体是一样的&#xff0c;其区别如下&#xff1a; 1、位段的成员必须是int 、unsigned int 、signed int 、在C99中位段的成员的类型也可以选择其他类型。 2、位段的成员名后边有一个冒号和一个数字 如下&#xff…

【大模型系列篇】硅基智能开源数字人模型HeyGem.ai,开启数字人时刻

硅基智能开源数字人模型HeyGem.ai, 1秒克隆生成4K视频, 支持离线多语言, 开源72小时狂揽1.3k星, 目前已经获得3.4k星。 硅基智能正式宣布在GitHub开源全球TOP级数字人模型&#xff0c;同时发布基于该模型的同名数字人工具硅基数字人克隆的本地安装包&#xff0c;这一举措标志着…

【C++】STL库面试常问点

STL库 什么是STL库 C标准模板库&#xff08;Standard Template Libiary&#xff09;基于泛型编程&#xff08;模板&#xff09;&#xff0c;实现常见的数据结构和算法&#xff0c;提升代码的复用性和效率。 STL库有哪些组件 STL库由以下组件构成&#xff1a; ● 容器&#xf…

knowledge-微前端(多个前端应用聚合的一个应用架构体系,每个小的应用可独立运行,独立开发,独立部署上线)

1.前言 微前端&#xff0c;将一个大的前端应用拆分为多个小型的&#xff0c;独立开发的前端应用&#xff0c;每一个小型的应用都可以单独的开发&#xff0c;部署和运行。这种结构允许不同的团队使用不同的技术栈来开发应用的不同部分&#xff0c;提高开发的效率与灵活性。 2.实…

三格电子PLC数据采集网关-工业互联的智能枢纽

在工业自动化领域&#xff0c;设备间的数据互通与协议兼容是核心挑战之一。三格电子推出的PLC据采集网关SG-PLC-Private&#xff0c;凭借其多协议兼容、高稳定性和灵活配置能力&#xff0c;成为工业物联网&#xff08;IIoT&#xff09;中实现设备互联的关键设备。本文将从产品功…