3个线程,分别打印A,B,C,循环打印ABC十次

本文介绍了一个使用Java多线程实现循环打印ABC字符的应用案例。通过三个线程分别控制打印A、B、C字符,并利用synchronized关键字和wait/notify机制确保了线程间的有序执行。文章展示了如何通过共享对象来协调线程执行顺序,最终实现按顺序循环打印ABC十次。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package test.thread;

/**3个线程,分别打印A,B,C,循环打印ABC十次
 * @author lsh
 *
 */
public class TestABC {

	public static void main(String[] args) throws Exception {
		ABC abc = new ABC();	//多线程中用来锁住对象 操作,已实现同步功能
		A a = new A(abc);
		B b = new B(abc);
		C c = new C(abc);
		Thread tA = new Thread(a);
		Thread tB = new Thread(b);
		Thread tC = new Thread(c);
		tC.start();
		tB.start();
		tA.start();
		tA.join();	//等待线程完成
		tB.join();
		tC.join();
		System.out.println("完成");
	}
}

class ABC {
	int i = 1;	//打印ABC的标志,1打印A,2打印B,3打印C
}

/**	线程A 打印 字符A
 * @author lsh
 *
 */
class A implements Runnable {
	ABC abc;

	public A(ABC abc) {
		this.abc = abc;
	}

	private void prin() {
		synchronized (abc) {
			try {
				for (int i = 0; i < 10; i++) {
					while (abc.i != 1) {
						abc.wait();	//等待
					}
					System.out.print("A");
					abc.i = 2;
					abc.notifyAll();//释放对象锁
				}

			} catch (Exception e) {
				e.printStackTrace();
				abc.notifyAll();
			}
		}
	}

	@Override
	public void run() {
		prin();
	}

}

/**	线程B 打印 字符B
 * @author lsh
 *
 */
class B implements Runnable {
	ABC abc;

	public B(ABC abc) {
		this.abc = abc;
	}

	private void prin() {
		synchronized (abc) {
			try {
				for (int i = 0; i < 10; i++) {
					while (abc.i != 2) {
						abc.wait();
					}
					System.out.print("b");
					abc.i = 3;
					abc.notifyAll();
				}
			} catch (Exception e) {
				e.printStackTrace();
				abc.notifyAll();
			}
		}
	}

	@Override
	public void run() {
		prin();
	}

}

/**	线程C 打印 字符C
 * @author lsh
 *
 */
class C implements Runnable {
	ABC abc;

	public C(ABC abc) {
		this.abc = abc;
	}

	private void prin() {
		synchronized (abc) {
			try {
				for (int i = 0; i < 10; i++) {
					while (abc.i != 3) {
						abc.wait();
					}
					System.out.println("c"+"   "+(i+1)+"次");
					abc.i = 1;
					abc.notifyAll();
				}
			} catch (Exception e) {
				e.printStackTrace();
				abc.notifyAll();
			}
		}
	}

	@Override
	public void run() {
		prin();
	}

}

<think>好的,我现在需要帮用户解决用Java实现三个线程顺序打印abc十次的问题。用户之前已经得到了Python的解决方案,现在想用Java来实现同样的功能。 首先,我得回顾一下Python代码的结构。Python中用了threading模块的Condition和Lock,通过条件变量来控制线程的执行顺序。每个线程打印自己的字符前会检查current_char_index,确保按顺序执行。打印完c后换行并增加计数器。 Java的多线程机制和Python有所不同,但同样有类似的条件变量和锁机制。Java中可以使用ReentrantLock和Condition来实现类似的功能。或者,使用synchronized关键字结合wait()和notifyAll()方法,这可能更接近Python的实现方式。 接下来,我需要考虑Java中如何实现线程间的同步。Java的每个对象都有一个内置锁,当使用synchronized方法或代码块时,会自动获取这个锁。wait()和notifyAll()方法必须在同步块内调用,否则会抛出IllegalMonitorStateException异常。 用户的需求是三个线程分别打印a、b、c,共十次,输出连续的十行abc。每个线程负责一个字符,必须严格按照顺序执行。因此,需要共享一个状态变量,记录当前应该打印哪个字符。例如,使用一个变量currentState,0代表a,1代表b,2代表c,循环变化。 在Java中,可以创建一个共享对象,包含currentState和count变量。每个线程在运行时检查currentState是否符合自己的角色,如果不符合,则调用wait()等待;符合则打印字符,更新状态,并通知所有其他线程继续执行。 需要注意的是,Java的wait()会释放锁,而notifyAll()会唤醒所有等待的线程,这样它们会竞争锁,但只有符合currentState的线程才会继续执行,其他的会再次等待。这样可以确保执行顺序。 可能的实现步骤: 1. 创建一个共享资源类,比如SharedResource,包含currentState和count。 2. 定义三个Runnable实现,每个负责打印a、b、c。 3. 每个Runnable在循环中检查currentState,如果不符合则等待。 4. 打印后更新currentState,并调用notifyAll()唤醒其他线程。 5. 当count达到10时,所有线程结束。 另外,需要考虑线程安全,所有对共享变量的修改都必须在同步块内进行,避免竞态条件。同时,循环检查条件时应该使用while而不是if,以防止虚假唤醒。 现在,根据这些思考,可以开始编写Java代码。使用synchronized关键字和wait/notifyAll方法,或者使用ReentrantLock和Condition,两者都可以。这里可能采用synchronized的方式,因为更接近用户之前的Python代码结构。 需要注意的是,在Java中,每个线程的任务可能需要循环执行多次,但每个字符需要打印十次,但每个线程需要打印十次各自的字符,还是总共十次abc?用户的问题中,每个线程打印十次各自的字符,但组合起来是十次abc。例如,a线程打印十次a,但每次打印a后需要等b和c打印完毕才能再次打印。不过原Python代码中的循环是每个线程执行10次循环,但实际每个循环打印一次a,然后等待整个abc完成后再次打印。所以可能需要每个线程执行十次打印各自的字符,但必须按顺序。 不过原题是打印出十组连续的abc,每个字母各打印10次,所以总共每个字母打印10次,组成10组abc。所以每个线程需要打印10次自己的字母,但必须严格按顺序,每次打印完自己的字母后,轮到下一个线程。例如,线程A打印a,然后线程B打印b,然后线程C打印c,然后重复这个过程十次。 因此,每个线程需要执行十次打印,但每次打印必须在正确的轮次中。因此,共享变量可能需要记录当前应该打印的字母,以及已经完成的组数。 在Java中,可以这样设计: - 一个共享对象,包含currentChar(0,1,2)和count(已完成的组数,初始0)。 - 三个线程,每个负责在currentChar为特定值时打印对应的字母,并更新currentChar,同时当打印完c后,count加1。 - 使用synchronized块来同步,并在每次打印后唤醒所有线程。 编写代码时,每个线程的run方法中循环,直到count达到10。在每次循环中,检查currentChar是否是自己应该处理的值,如果是,则打印,更新currentChar,如果是c则增加count,然后唤醒所有线程。否则,等待。 需要注意,在循环中检查条件时,必须使用while循环,防止虚假唤醒。 例如: class SharedResource { int currentChar = 0; int count = 0; } 线程A: public void run() { synchronized (shared) { while (shared.count < 10) { while (shared.currentChar != 0) { try { shared.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (shared.count >= 10) break; System.out.print("a"); shared.currentChar = 1; shared.notifyAll(); } } } 类似地,线程B和C处理currentChar为1和2的情况,线程C在打印后增加count,并换行。 这样,每个线程会等待直到轮到自己,打印后唤醒其他线程,直到完成十次循环。 现在,把这些思路整理成Java代码,并测试是否正确。例如,输出应该是十行abc,每行后面有一个换行。在Python代码中,线程C打印c后换行,所以在Java中,线程C在打印c后加上System.out.println()或者打印"c\n"。 但要注意,在Java中,System.out.print("c\n")和System.out.println("c")效果相同。但在多线程中,打印语句需要同步,否则可能出现交叉输出。不过由于在synchronized块中执行打印,所以可以保证原子性,不会出现交叉。 现在,编写完整的Java代码: public class PrintABC { static class SharedResource { int currentChar = 0; int count = 0; } public static void main(String[] args) { SharedResource shared = new SharedResource(); Runnable printA = () -> { synchronized (shared) { while (shared.count < 10) { while (shared.currentChar != 0) { try { shared.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (shared.count >= 10) break; System.out.print("a"); shared.currentChar = 1; shared.notifyAll(); } } }; Runnable printB = () -> { synchronized (shared) { while (shared.count < 10) { while (shared.currentChar != 1) { try { shared.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (shared.count >= 10) break; System.out.print("b"); shared.currentChar = 2; shared.notifyAll(); } } }; Runnable printC = () -> { synchronized (shared) { while (shared.count < 10) { while (shared.currentChar != 2) { try { shared.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (shared.count >= 10) break; System.out.println("c"); shared.currentChar = 0; shared.count++; shared.notifyAll(); } } }; new Thread(printA).start(); new Thread(printB).start(); new Thread(printC).start(); } } 这样,每个线程在同步块中循环,检查currentChar是否是自己该执行的时候。当线程C打印后,count加1,并将currentChar重置为0,开始下一轮。 测试这个代码是否正确,是否输出十行abc,并且不会死锁。 需要注意的问题: 1. 所有对共享变量的操作都必须在同步块内。 2. 使用while循环检查条件,避免虚假唤醒。 3. 在线程C中,打印完c后换行,并增加count。 4. 在每次notifyAll后,其他线程被唤醒,但只有符合条件的线程会继续执行。 这样应该能正确实现需求。</think>以下是使用 Java 实现三个线程按顺序打印十组 `abc` 的代码,通过 **`synchronized` 锁**和 **`wait/notifyAll`** 机制控制执行顺序: ```java public class PrintABC { // 共享状态对象 private static final Object lock = new Object(); private static int currentChar = 0; // 0:a, 1:b, 2:c private static int count = 0; // 记录已完成的组数 public static void main(String[] args) { Runnable printA = () -> printChar(&#39;a&#39;, 0); Runnable printB = () -> printChar(&#39;b&#39;, 1); Runnable printC = () -> printChar(&#39;c&#39;, 2); new Thread(printA).start(); new Thread(printB).start(); new Thread(printC).start(); } private static void printChar(char ch, int targetState) { synchronized (lock) { while (count < 10) { // 最多打印10组 // 如果当前状态不匹配,线程等待 while (currentChar != targetState) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 打印字符并更新状态 if (ch == &#39;c&#39;) { System.out.println(ch); // 打印c后换行 count++; // 完成一组 } else { System.out.print(ch); // 打印a或b } currentChar = (currentChar + 1) % 3; // 更新状态 lock.notifyAll(); // 唤醒其他线程 } } } } ``` --- ### 核心逻辑说明 1. **共享状态**: - `currentChar`:标记当前应打印的字符(0表示`a`,1表示`b`,2表示`c`)。 - `count`:记录已完成的`abc`组数,达到10时终止所有线程。 2. **同步机制**: - 使用 `synchronized(lock)` 保证线程安全。 - 线程通过 `while (currentChar != targetState)` 检查是否轮到自己: - 如果条件不满足,调用 `lock.wait()` 释放锁并等待。 - 条件满足时打印字符,更新状态,并通过 `lock.notifyAll()` 唤醒其他线程3. **终止条件**: - 当 `count >= 10` 时,所有线程退出循环,结束任务。 --- ### 输出示例 ``` abc abc abc ...(共10行) ``` --- ### 扩展问题解答 1. **如何避免竞态条件?** 所有对共享变量(如 `currentChar` 和 `count`)的读写均在 `synchronized` 块内完成,确保原子性。 2. **如何调整打印内容?** 修改 `printChar` 方法中的字符参数即可(如 `printChar(&#39;x&#39;, 0)` 打印`x`)。 3. **是否有更简洁的实现?** 可以使用 `ReentrantLock` 和 `Condition` 替代 `synchronized`,但核心逻辑类似。例如: ```java private static final ReentrantLock reentrantLock = new ReentrantLock(); private static final Condition condition = reentrantLock.newCondition(); ``` 通过 `condition.await()` 和 `condition.signalAll()` 实现更细粒度的控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值