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

题目:

三个线程,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的字符串数组和字符串的区别以及使用




  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值