三个线程循环输出ABCABCABC....

本文介绍了一种利用Java中的synchronized、wait和notify方法实现多线程按顺序输出ABC的解决方案。通过实例代码详细解释了如何避免IllegalMonitorStateException异常,并讨论了String对象与字符数组在同步中的应用。

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

题目:

三个线程,A线程输出A,B线程输出B,C线程输出C

然后这三个线程运行循环输出ABCABCABCABC......


此题目考察的是线程的同步,多线程的控制


解决方案中主要用到了java中的sychronised, notify, wait

notify和wait主要是为了控制线程,控制程序的执行流程


  • 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
  • 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
  • 如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。

其中wait方法有三个over load方法:

wait()

wait(long)

wait(long,int)

wait方法通过参数可以指定等待的时长。如果没有指定参数,默认一直等待直到被通知。

这些方法只能在同步方法或同步块内部调用。如果当前线程不是对象所得持有者,该方法抛出一个java.lang.IllegalMonitorStateException 异常

这些方法需要在同步块中使用,并且调用这些方法的对象要是同步代码块中要求的同步对象,也就是monitor


java.lang.IllegalMonitorStateException 异常
违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,抛出该异常。



首先给出解决的代码:

public class Test{
	
	private static String[] flag = {"A"};
	static class AThread extends Thread{

		@Override
		public void run() {
			// TODO Auto-generated method stub
			int i = 0;
			while(i<10){
				synchronized (flag) {
					if(flag[0].equals("A")){
						System.out.print("A");
						flag[0] = "B";
						
						i++;
					}
					flag.notify();
					try {
						flag.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		
	}
	
	static class BThread extends Thread{
		@Override
		public void run() {
			// TODO Auto-generated method stub
			int i = 0;
			while(i<10){
				synchronized (flag) {
					if(flag[0].equals("B")){
						System.out.print("B");
						flag[0] = "C";
						i++;
					}
					flag.notify();
					try {
						flag.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			}
		}
	
	static class CThread extends Thread{
		@Override
		public void run() {
			// TODO Auto-generated method stub
			int i = 0;
			while(i<10){
				synchronized (flag) {
					if(flag[0].equals("C")){
						System.out.println("C");
						flag[0] = "A";
						
						i++;
					}
					flag.notify();
					try {
						flag.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
	
	public static void main(String[] args) throws InterruptedException{
		AThread thread1 = new AThread();
		BThread thread2 = new BThread();
		CThread thread3 = new CThread();
			thread1.start();
			thread2.start();
			thread3.start();
	}
	
}
使用了一个全局变量flag来作为monitor来进行同步

因为在sychronised代码块中对flag进行了更改,所以flag不能用String对象,因为String对象是不可变对象,对其进行了修改,则在下面的代码中的flag.wait()就会抛出异常.因为同步的monitor是没有更改之前的对象,而在内部又对flag重新赋值了,所以导致调用wait函数的对象和前面同步的monitor不一致,抛出了java.lang.IllegalMonitorStateException 异常


因此在此处使用了字符数组来代替之前的String对象


字符数组的说明:

java中字符数组的内存表示如下图所示:



因为字符串对象变化之后栈区的指针会指向其他的常量区地址,而字符串数组中栈区的引用指向的堆区的地址是不变的,而当改变数组元素时变化的是堆区每个元素空间指向的地址.

因此可以用数组来作为一个全局变量来作为monitor来作为同步.


对线程同步的讲解:

首先获得monitor对象,再判断flag的值是不是"A" "B" "C",来进行对应的处置,如果不是正确的顺序的线程获得了monitor的权限,则不会进入if语句,会直接notify和wait,让其他的线程获得monitor自己阻塞等待.

借助了外部的flag全局变量来使ABC按书序输出,但是并没有实现,A线程运行完就运行B线程,B线程运行完就运行C线程,这样实现线程的按照一定的顺序运行.


另外notify和notifyAll并不能notify某个固定的线程,只是唤醒等待的线程中的某一个或所有,并不能指定唤醒哪一个线程.


wait和sleep的区别就是,wait会释放自己获取的锁,而sleep不释放锁,只是阻塞一定的时间而已.



总结:

1,多线程的同步,使用synchronized来进行同步,线程间通信就使用wait() 和 notify() 函数来让线程按照一定的安排获得CPU使用权运行

2,多线程中的java.lang.IllegalMonitorStateException 异常要注意

3,java中的String等有些内置不可变对象尽量不要作为synchronized的monitor, 要使用可变对象作为monitor,这样便可以在同步代码块内部修改monitor

4,java的字符串数组和字符串的区别以及使用




为了实现三个线程循环打印ABC,可以使用Synchronized同步方法和Object的wait()和notify()方法。首先,创建三个线程A、B、C,并设置它们的打印次数为10。然后,通过使用三个对象锁a、b、c来控制线程的执行顺序。A线程首先获得c对象锁,打印A后释放c对象锁,并通过notify()方法唤醒B线程。B线程等待a对象锁,获取到a对象锁后打印B,并释放a对象锁,然后通过notify()方法唤醒C线程。C线程等待b对象锁,获取到b对象锁后打印C,并释放b对象锁,并通过notify()方法唤醒A线程。这样就实现了三个线程循环打印ABC的需求。 以下是一个示例代码: ```java class PrintThread implements Runnable { private static final Object a = new Object(); private static final Object b = new Object(); private static final Object c = new Object(); private String name; public PrintThread(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 10; i++) { synchronized (name) { try { switch (name) { case "A": synchronized (c) { System.out.print("A"); c.notify(); } name.wait(); break; case "B": synchronized (a) { System.out.print("B"); a.notify(); } name.wait(); break; case "C": synchronized (b) { System.out.print("C"); b.notify(); } name.wait(); break; } } catch (InterruptedException e) { e.printStackTrace(); } } } } } public class Main { public static void main(String[] args) { Thread threadA = new Thread(new PrintThread("A")); Thread threadB = new Thread(new PrintThread("B")); Thread threadC = new Thread(new PrintThread("C")); threadA.start(); threadB.start(); threadC.start(); } } ``` 通过以上代码,三个线程将按照ABCABCABC的顺序循环打印10次。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [多线程交替打印ABC的多种实现方法](https://blog.csdn.net/xiaokang123456kao/article/details/77331878)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [三个线程轮流打印ABC](https://blog.csdn.net/yu1336199790/article/details/118725454)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值