三个线程交替打印ABC

4.三个线程交替打印ABC,循环100次,如何实现?用wait/notify和Lock/Condition分别实现。

方案一:使用 synchronized 和 wait/notify 实现

synchronized是Java中的一个关键字,用于实现对共享资源的互斥访问。wait和notify是Object类中的两个方法,用于实现线程间的通信。wait方法会让当前线程释放锁,并进入等待状态,直到被其他线程唤醒。notify方法会唤醒一个在同一个锁上等待的线程。

使用一个共享变量state来表示当前应该打印哪个字母,初始值为0。当state为0时,表示轮到A线程打印;当state为1时,表示轮到B线程打印;当state为2时,表示轮到C线程打印。每个线程在打印完字母后,需要将state更换为下一个要打印的值。同时,每个线程还需要唤醒下一个线程,并让自己进入等待状态。

java 复制代码
public class PrintABCWithWaitNotify {
    // 共享对象,作为锁和通信的媒介
    private final Object lock = new Object();
    // 共享变量,表示当前应该打印的字母
    private volatile int state = 0; // 0: 打印A, 1: 打印B, 2: 打印C
    
    public void printA() {
        synchronized (lock) {
            for (int i = 0; i < 100; i++) {
                while (state != 0) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
                System.out.print("A");
                state = 1;
                lock.notifyAll();
            }
        }
    }
    
    public void printB() {
        synchronized (lock) {
            for (int i = 0; i < 100; i++) {
                while (state != 1) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
                System.out.print("B");
                state = 2;
                lock.notifyAll();
            }
        }
    }
    
    public void printC() {
        synchronized (lock) {
            for (int i = 0; i < 100; i++) {
                while (state != 2) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
                System.out.print("C");
                state = 0;
                lock.notifyAll();
            }
        }
    }
    
    public static void main(String[] args) {
        PrintABCWithWaitNotify printer = new PrintABCWithWaitNotify();
        
        Thread threadA = new Thread(printer::printA);
        Thread threadB = new Thread(printer::printB);
        Thread threadC = new Thread(printer::printC);
        
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

方案二:使用 ReentrantLock/Condition 实现

ReentrantLock是Java中的一个类,用于实现可重入的互斥锁。Condition是ReentrantLock中的一个接口,用于实现线程间的条件等待和唤醒。ReentrantLock可以创建多个Condition对象,每个Condition对象可以绑定一个或多个线程,实现对不同线程的精确控制。

使用一个ReentrantLock对象作为锁,同时创建三个Condition对象,分别绑定A、B、C三个线程。每个线程在打印字母之前,需要调用对应的Condition对象的await方法,等待被唤醒。每个线程在打印字母之后,需要调用下一个Condition对象的signal方法,唤醒下一个线程。

java 复制代码
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class PrintABCWithLockCondition {
    // 可重入锁
    private final ReentrantLock lock = new ReentrantLock();
    // 三个条件对象,分别绑定A、B、C三个线程
    private final Condition conditionA = lock.newCondition();
    private final Condition conditionB = lock.newCondition();
    private final Condition conditionC = lock.newCondition();
    // 共享变量,表示当前应该打印的字母
    private volatile int state = 0; // 0: 打印A, 1: 打印B, 2: 打印C
    
    public void printA() {
        lock.lock();
        try {
            for (int i = 0; i < 100; i++) {
                while (state != 0) {
                    conditionA.await();
                }
                System.out.print("A");
                state = 1;
                conditionB.signal(); // 唤醒线程B
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
    
    public void printB() {
        lock.lock();
        try {
            for (int i = 0; i < 100; i++) {
                while (state != 1) {
                    conditionB.await();
                }
                System.out.print("B");
                state = 2;
                conditionC.signal(); // 唤醒线程C
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
    
    public void printC() {
        lock.lock();
        try {
            for (int i = 0; i < 100; i++) {
                while (state != 2) {
                    conditionC.await();
                }
                System.out.print("C");
                state = 0;
                conditionA.signal(); // 唤醒线程A
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
    
    public static void main(String[] args) {
        PrintABCWithLockCondition printer = new PrintABCWithLockCondition();
        
        Thread threadA = new Thread(printer::printA);
        Thread threadB = new Thread(printer::printB);
        Thread threadC = new Thread(printer::printC);
        
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

方案三:使用 Semaphore 实现

Semaphore是Java中的一个类,用于实现信号量机制。信号量是一种计数器,用于控制对共享资源的访问。Semaphore可以创建多个信号量对象,每个信号量对象可以绑定一个或多个线程,实现对不同线程的精确控制。

使用三个Semaphore对象,分别初始化为1、0、0,表示A、B、C三个线程的初始许可数。每个线程在打印字母之前,需要调用对应的Semaphore对象的acquire方法,获取许可。每个线程在打印字母之后,需要调用下一个Semaphore对象的release方法,释放许可。

java 复制代码
import java.util.concurrent.Semaphore;

public class PrintABCWithSemaphore {
    private final Semaphore semaphoreA = new Semaphore(1); // 初始允许A先执行
    private final Semaphore semaphoreB = new Semaphore(0);
    private final Semaphore semaphoreC = new Semaphore(0);
    
    public void printA() {
        for (int i = 0; i < 100; i++) {
            try {
                semaphoreA.acquire(); // 获取信号量
                System.out.print("A");
                
                // 释放B的信号量
                semaphoreB.release();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            }
        }
    }
    
    public void printB() {
        for (int i = 0; i < 100; i++) {
            try {
                semaphoreB.acquire(); // 获取信号量
                System.out.print("B");
                
                // 释放C的信号量
                semaphoreC.release();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            }
        }
    }
    
    public void printC() {
        for (int i = 0; i < 100; i++) {
            try {
                semaphoreC.acquire(); // 获取信号量
                System.out.print("C");
                
                // 释放A的信号量
                semaphoreA.release();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            }
        }
    }
    
    public static void main(String[] args) {
        PrintABCWithSemaphore printer = new PrintABCWithSemaphore();
        
        Thread threadA = new Thread(printer::printA);
        Thread threadB = new Thread(printer::printB);
        Thread threadC = new Thread(printer::printC);
        
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

方案四:使用 AtomicInteger 和 CAS 实现

AtomicInteger是Java中的一个类,用于实现原子性的整数操作。CAS是一种无锁的算法,全称为Compare And Swap,即比较并交换。CAS操作需要三个参数:一个内存地址,一个期望值,一个新值。如果内存地址的值与期望值相等,就将其更新为新值,否则不做任何操作。

使用一个AtomicInteger对象来表示当前应该打印哪个字母,初始值为0。当state为0时,表示轮到A线程打印;当state为1时,表示轮到B线程打印;当state为2时,表示轮到C线程打印。每个线程在打印完字母后,需要使用CAS操作将state加1,并对3取模,以便循环。

java 复制代码
import java.util.concurrent.atomic.AtomicInteger;

public class PrintABCWithAtomicInteger {
    private final AtomicInteger state = new AtomicInteger(0); // 0: 打印A, 1: 打印B, 2: 打印C
    
    public void printA() {
        int count = 0;
        while (count < 100) {
            if (state.get() == 0) {
                if (state.compareAndSet(0, 1)) {
                    System.out.print("A");
                    count++;
                }
            }
        }
    }
    
    public void printB() {
        int count = 0;
        while (count < 100) {
            if (state.get() == 1) {
                if (state.compareAndSet(1, 2)) {
                    System.out.print("B");
                    count++;
                }
            }
        }
    }
    
    public void printC() {
        int count = 0;
        while (count < 100) {
            if (state.get() == 2) {
                if (state.compareAndSet(2, 0)) {
                    System.out.print("C");
                    count++;
                }
            }
        }
    }
    
    public static void main(String[] args) {
        PrintABCWithAtomicInteger printer = new PrintABCWithAtomicInteger();
        
        Thread threadA = new Thread(printer::printA);
        Thread threadB = new Thread(printer::printB);
        Thread threadC = new Thread(printer::printC);
        
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

各种方法对比总结

方法 优点 缺点 适用场景
wait/notify JDK原生支持,简单易懂 会唤醒所有等待线程,效率较低 学习和理解同步机制
Lock/Condition 精确控制线程唤醒,性能好 代码相对复杂 高并发场景
Semaphore 控制流程清晰,易于理解 信号量初始值设置需注意 资源访问控制
AtomicInteger/CAS 无锁设计,性能高 CPU消耗较高,可能出现忙等 高性能要求场景

【参考文献】

1、https://cloud.tencent.com/developer/article/2212386

2、https://cloud.tencent.com/developer/article/2300487

相关推荐
凯瑟琳.奥古斯特2 小时前
SpringBoot快速入门指南
java·开发语言·spring boot·后端·spring
是席木木啊2 小时前
Tomcat CVE-2026-34483安全漏洞警告问题总结与修复方案
java·tomcat·firefox
代码漫谈2 小时前
基于 Spring Boot 3.2.x 的 Actuator 监控指南:从健康检查到企业级监控体系
java·spring boot·actuator 监控
WL_Aurora2 小时前
Java基础知识超详细总结(从入门到精通)
java
咖啡八杯2 小时前
GoF设计模式——抽象工厂模式
java·后端·spring·设计模式·抽象工厂模式
Thanks_ks2 小时前
分布式锁:Redis 与 Redisson 的工程实践与避坑指南
java·redis·分布式锁·redisson·微服务架构·并发编程·高可用
掉鱼的猫2 小时前
agentscope-harness vs solon-ai-harness:Java 智能体「马具引擎」的双雄对决
java·openai
RainCity2 小时前
Java Swing 自定义组件库分享(四)
java·笔记·后端
带刺的坐椅2 小时前
agentscope-harness vs solon-ai-harness:Java 智能体「马具引擎」的双雄对决
java·ai·llm·solon·agentscope·harness