`

考察wait notify 交互的一道面试题目

阅读更多
前两天去面试 有一道题目,答的很不满意。先是理解错题意,后是感觉做不出索性交卷,在等待面试中想清楚了这道题的思路。
当时那会真是糊涂,最初想的是考察join的含义, 题目写了一半感觉不对头,后来考虑 java 5 提供的各种多线程控制组件(好久不用,不熟悉了啊),也没想出办法,其实用最基本的wait notify 就能实现了,真真不该早叫卷。。。

join:是等待另外一个线程的完全结束。现在需要循环打印,显然是不行的。

题目想考察的是线程间的交互控制,即 wait notify 来实现的交互

题目:启动三个线程,分别打印A B C,现在写一个程序 循环打印ABCABCABC....



package com.dajie.position.thread;

/**
 * 输出A字符任务
 * @author xinchun.wang
 *
 */
class TaskA implements Runnable {
    private Object lockC;
    private Object lockA;

    public TaskA(Object lockC,Object lockA) {
	this.lockC = lockC;
	this.lockA = lockA;
    }

    @Override
    public void run() {
	boolean falg = true;
	while (true) {
	    synchronized (lockC) {
		try {
		    if (!falg) {
			lockC.wait();
		    }
		} catch (InterruptedException e) {
		    e.printStackTrace();
		    return;
		}
		falg = false;
		System.out.println("A");
	    }
	    
	    try {
		    Thread.sleep(1000);
		} catch (InterruptedException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
	    
	   synchronized(lockA){
	       lockA.notify();
	   }
	}
    }

}

/**
 * 输出B字符串任务
 * @author xinchun.wang
 *
 */
class TaskB implements Runnable {
    private Object lockA;
    private Object lockB;
    public TaskB(Object lockA,Object lockB) {
	this.lockA = lockA;
	this.lockB = lockB;
    }

    @Override
    public void run() {
	while (true) {
	    synchronized (lockA) {
		try {
		    lockA.wait();
		} catch (InterruptedException e) {
		    e.printStackTrace();
		    return;
		}
		System.out.println("B");
	    }
	    
	    try {
		    Thread.sleep(1000);
		} catch (InterruptedException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
	    
	    synchronized (lockB) {
		lockB.notify();
	    }
	}
    }

}

/**
 * 输出C字符串任务
 * @author xinchun.wang
 *
 */
class TaskC implements Runnable {
    private Object lockB;
    private Object lockC;
    public TaskC(Object lockB,Object lockC) {
	this.lockB = lockB;
	this.lockC = lockC;
    }

    @Override
    public void run() {
	while (true) {
	    synchronized (lockB) {
		try {
		    lockB.wait();
		} catch (InterruptedException e) {
		    e.printStackTrace();
		    return;
		}
		System.out.println("C");
	    }
	    
	    try {
		    Thread.sleep(1000);
		} catch (InterruptedException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
	    
	    synchronized (lockC) {
		lockC.notify();
	    }
	}

    }

}

/**
 * 测试例子
 * @author xinchun.wang
 *
 */
public class PrintData {
    public static void main(String[] args) {
	Object lockA = new Object();
	Object lockB = new Object();
	Object lockC = new Object();

	Thread tA = new Thread(new TaskA(lockC,lockA));
	Thread tB = new Thread(new TaskB(lockA,lockB));
	Thread tC = new Thread(new TaskC(lockB,lockC));
	tA.start();
	tB.start();
	tC.start();

    }
}


0
1
分享到:
评论
17 楼 daojin 2013-05-11  
heirenhua 写道
我觉得比较好的是CSDN上的某大牛给的答案。

http://bbs.csdn.net/topics/360111428?page=1#post-394423315

45楼

呵呵,里面有个答案挺不错。
16 楼 heirenhua 2013-05-08  
我觉得比较好的是CSDN上的某大牛给的答案。

http://bbs.csdn.net/topics/360111428?page=1#post-394423315

45楼
15 楼 zjumty 2013-05-05  
daojin 写道

按道理,这个锁应该是动态的,一开始是不需要陷入内核的,如果等待时间过长,就陷入内核。这样效率才高。


你说的没错, 我看的是openjdk 7的代码。 JVM并不是会直接使用WaitForSingleObject之类的调用陷入内核模式。而是会先Spin一定的周期,并且还有很多判断,只有在"万不得已"的情况下才会陷入内核模式。 可能DVM是面向移动设备设计的,所以在并发上更加重视效率吧。
14 楼 daojin 2013-05-05  
你好,我刚才查了一下google的代码,在google安卓的源代码中没有找到semophore的影子。就连消息队列都是用wait,notify来写的。google工程师不可能不知道semophore的方便性的,但是还是没有使用它。我觉得最重要的原因就是效率问题,semophore的实现没有进行优化。不同的java虚拟机,不同的操作系统平台之间差异比较大。

效率问题是拿不陷入内核消耗的CPU时间片和陷入内核消耗的时间片进行对比。

按道理,这个锁应该是动态的,一开始是不需要陷入内核的,如果等待时间过长,就陷入内核。这样效率才高。

另外一个问题是,JVM是不是按照上面说的实现的?

目前我从网上得到的信息http://www.blogjava.net/xiaohuzi2008/archive/2012/07/17/383344.html
这篇文章。确实是这样的。但是我没有源代码,也不知道怎么找。你熟悉的话帮忙找一下在什么地方,共同学习进步。

另外,安卓虚拟机DVM中,semophore的实现也不是动态的,并且是不需要陷入内核的。直接用汇编来写的。

    

    int32_t prev;
    __asm__ __volatile__ ("lock; cmpxchgl %1, %2"
                          : "=a" (prev)
                          : "q" (new_value), "m" (*ptr), "0" (old_value)
                          : "memory");
    return prev != old_value;




13 楼 daojin 2013-05-05  
zjumty 写道
daojin 写道

不过简洁的不一定是最好的。如果考虑效率问题,还是java的synchronized,wait,notify机制效率高。因为信号量需要陷入内核。而wait的实现时,可以不需要陷入内核。
效率提高十倍以上。


用synchronized,wait,notify确实可以实现出比Semaphore更高效的方式。
但是“信号量需要陷入内核。而wait的实现时,可以不需要陷入内核”不是很理解。

是乎synchronized方式和Semaphore(LockSupport.park),到了jvm那一层都是ObjectMonitor.cpp这个类实现的,而这个类的同步方法里,经过判断确实需要将线程挂起时,都是调用_ParkEvent->park ()方法, 这个方法在各个OS上实现略有不同, 在WIndows上是CreateEvent和WaitForSingleObjec(),在Linux上是ptread的pthread_cond_wait。

我对操作系统不是十分了解,使用上述的机制难道不是要切换到内核模式吗?

求解答。


陷入内核的话很明显的效率低下。你说的那些都会陷入内核。是重量级的锁机制。你一定是找错代码了。或者你的虚拟机版本在1.6以下。1.6以上不应该是这个机制。
12 楼 zjumty 2013-05-05  
daojin 写道

不过简洁的不一定是最好的。如果考虑效率问题,还是java的synchronized,wait,notify机制效率高。因为信号量需要陷入内核。而wait的实现时,可以不需要陷入内核。
效率提高十倍以上。


用synchronized,wait,notify确实可以实现出比Semaphore更高效的方式。
但是“信号量需要陷入内核。而wait的实现时,可以不需要陷入内核”不是很理解。

是乎synchronized方式和Semaphore(LockSupport.park),到了jvm那一层都是ObjectMonitor.cpp这个类实现的,而这个类的同步方法里,经过判断确实需要将线程挂起时,都是调用_ParkEvent->park ()方法, 这个方法在各个OS上实现略有不同, 在WIndows上是CreateEvent和WaitForSingleObjec(),在Linux上是ptread的pthread_cond_wait。

我对操作系统不是十分了解,使用上述的机制难道不是要切换到内核模式吗?

求解答。

11 楼 静湖孤子 2013-05-05  
package org.codinglife.printABC2;

public class PrintABC implements Runnable {

public static void main(String[] args) throws InterruptedException {
new Thread(new PrintABC()).start();
}

@Override
public void run() {
Thread printA = new RunPrint("A");
Thread printB = new RunPrint("B");
Thread printC = new RunPrint("C");

synchronized(printA) {
synchronized (printB) {
synchronized (printC) {
System.out.println("Main Dispatcher");
printA.start();
printB.start();
printC.start();

while(true) {
try {
printA.notify();
printA.wait();

printB.notify();
printB.wait();

printC.notify();
printC.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}

class RunPrint extends Thread {
private String printStr;

public RunPrint(String printStr) {
this.printStr = printStr;
}

@Override
public void run() {
while(true) {
synchronized (this) {
System.out.println(printStr);
this.notify();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

花时间写了一个, 练练手.
10 楼 daojin 2013-05-04  
sunlujing 写道
zjumty 写道
如果可以用Java5以后的Semaphore实现起来比较简单

public class ThreadSync {
    static class ConditionThread extends Thread {
        ConditionThread(Semaphore preCond, Semaphore postCond, String text) {
            this.preCond = preCond;
            this.postCond = postCond;
            this.text = text;
        }

        private Semaphore preCond;
        private Semaphore postCond;
        private String text;


        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    preCond.acquire();
                    System.out.print(text);
                    postCond.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphoreA = new Semaphore(0);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(1);

        Thread threadA = new ConditionThread(semaphoreC, semaphoreA, "A");
        Thread threadB = new ConditionThread(semaphoreA, semaphoreB, "B");
        Thread threadC = new ConditionThread(semaphoreB, semaphoreC, "C");

        threadA.start();
        threadB.start();
        threadC.start();

        threadA.join();
        threadB.join();
        threadC.join();
    }
}

这个方法比较简洁

不过简洁的不一定是最好的。如果考虑效率问题,还是java的synchronized,wait,notify机制效率高。因为信号量需要陷入内核。而wait的实现时,可以不需要陷入内核。
效率提高十倍以上。
9 楼 sunlujing 2013-05-04  
zjumty 写道
如果可以用Java5以后的Semaphore实现起来比较简单

public class ThreadSync {
    static class ConditionThread extends Thread {
        ConditionThread(Semaphore preCond, Semaphore postCond, String text) {
            this.preCond = preCond;
            this.postCond = postCond;
            this.text = text;
        }

        private Semaphore preCond;
        private Semaphore postCond;
        private String text;


        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    preCond.acquire();
                    System.out.print(text);
                    postCond.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphoreA = new Semaphore(0);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(1);

        Thread threadA = new ConditionThread(semaphoreC, semaphoreA, "A");
        Thread threadB = new ConditionThread(semaphoreA, semaphoreB, "B");
        Thread threadC = new ConditionThread(semaphoreB, semaphoreC, "C");

        threadA.start();
        threadB.start();
        threadC.start();

        threadA.join();
        threadB.join();
        threadC.join();
    }
}

这个方法比较简洁
8 楼 daojin 2013-05-04  
zjumty 写道
如果可以用Java5以后的Semaphore实现起来比较简单

public class ThreadSync {
    static class ConditionThread extends Thread {
        ConditionThread(Semaphore preCond, Semaphore postCond, String text) {
            this.preCond = preCond;
            this.postCond = postCond;
            this.text = text;
        }

        private Semaphore preCond;
        private Semaphore postCond;
        private String text;


        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    preCond.acquire();
                    System.out.print(text);
                    postCond.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphoreA = new Semaphore(0);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(1);

        Thread threadA = new ConditionThread(semaphoreC, semaphoreA, "A");
        Thread threadB = new ConditionThread(semaphoreA, semaphoreB, "B");
        Thread threadC = new ConditionThread(semaphoreB, semaphoreC, "C");

        threadA.start();
        threadB.start();
        threadC.start();

        threadA.join();
        threadB.join();
        threadC.join();
    }
}

这个写得不错。
信号量对资源访问的控制,有一个逻辑隐含在里面,而wait却没有。这就是信号量的强项。
7 楼 王新春 2013-05-04  
daojin 写道
 

同学,多线程很难啊,不过你的题目非常好。让我练习了一下多线程。谢谢。

不过,看你的基础不牢啊,再好好想想!多线程真的很难,不是一般的。程序搞了四五年了,现在才略知一二。

嗯,我考虑确实不周,其实题目提醒:不准用sleep ,所以我的答案是有问题的,谢谢提醒。不过多线程没有太难,只要你理解了。但是我们可能考虑不周。加油小伙。
6 楼 zjumty 2013-05-04  
如果可以用Java5以后的Semaphore实现起来比较简单

public class ThreadSync {
    static class ConditionThread extends Thread {
        ConditionThread(Semaphore preCond, Semaphore postCond, String text) {
            this.preCond = preCond;
            this.postCond = postCond;
            this.text = text;
        }

        private Semaphore preCond;
        private Semaphore postCond;
        private String text;


        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    preCond.acquire();
                    System.out.print(text);
                    postCond.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphoreA = new Semaphore(0);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(1);

        Thread threadA = new ConditionThread(semaphoreC, semaphoreA, "A");
        Thread threadB = new ConditionThread(semaphoreA, semaphoreB, "B");
        Thread threadC = new ConditionThread(semaphoreB, semaphoreC, "C");

        threadA.start();
        threadB.start();
        threadC.start();

        threadA.join();
        threadB.join();
        threadC.join();
    }
}
5 楼 daojin 2013-05-03  
 

同学,多线程很难啊,不过你的题目非常好。让我练习了一下多线程。谢谢。

不过,看你的基础不牢啊,再好好想想!多线程真的很难,不是一般的。程序搞了四五年了,现在才略知一二。
4 楼 daojin 2013-05-03  


package MyTesting;

/**
 * 输出A字符任务
 * @author xinchun.wang
 *
 */
class TaskA implements Runnable {
	
    private Object lockC;
    private Object lockA;

    public TaskA(Object lockC,Object lockA) {
	this.lockC = lockC;
	this.lockA = lockA;
    }

    @Override
    public void run() {
	
	while (true) {
	    synchronized (lockC) {
	    	try {
	    		while (!PrintData.mCHavPrint) {
	    			lockC.wait();
	    		}
			} catch (InterruptedException e) {
				e.printStackTrace();
				return;
			}
	    	 PrintData.mCHavPrint = false;
	     }
	    
	     System.out.println("A");
	     
	     synchronized(lockA){
	    	 PrintData.mAHavPrint = true;
	         lockA.notify();
	      }
	    }
    }

}

/**
 * 输出B字符串任务
 * @author xinchun.wang
 *
 */
class TaskB implements Runnable {
    private Object lockA;
    private Object lockB;
    public TaskB(Object lockA,Object lockB) {
	this.lockA = lockA;
	this.lockB = lockB;
    }

    @Override
    public void run() {
	while (true) {
		
	    synchronized (lockA) {
			try {
	    		while (!PrintData.mAHavPrint) {
	    			lockA.wait();
	    		}
			} catch (InterruptedException e) {
				e.printStackTrace();
				return;
			}
			  PrintData.mAHavPrint = false;
		
	    }
	  
	    
	    System.out.println("B");
	    synchronized (lockB) {
	    	PrintData.mBHavPrint= true;
	    	lockB.notify();
	    }
	}
    }

}

/**
 * 输出C字符串任务
 * @author xinchun.wang
 *
 */
class TaskC implements Runnable {
    private Object lockB;
    private Object lockC;
    public TaskC(Object lockB,Object lockC) {
	this.lockB = lockB;
	this.lockC = lockC;
    }

    @Override
    public void run() {
	while (true) {
	    synchronized (lockB) {
	    	try {
	    		while (!PrintData.mBHavPrint) {
	    			lockB.wait();
	    		}
			} catch (InterruptedException e) {
				e.printStackTrace();
				return;
			}
	    	  PrintData.mBHavPrint = false;
	    }
	    
	  
	    System.out.println("C");
	    
	    synchronized (lockC) {
	    	PrintData.mCHavPrint = true;
	    	lockC.notify();
	    }
	}

    }

}

/**
 * 测试例子
 * @author xinchun.wang
 *
 */
public class PrintData {
	 public static boolean mCHavPrint = true;
	 public static boolean mAHavPrint = false;
	 public static boolean mBHavPrint = false;
    public static void main(String[] args) {
    
   
    
	Object lockA = new Object();
	Object lockB = new Object();
	Object lockC = new Object();

	Thread tA = new Thread(new TaskA(lockC,lockA));
	Thread tB = new Thread(new TaskB(lockA,lockB));
	Thread tC = new Thread(new TaskC(lockB,lockC));
	tA.start();
	tB.start();
	tC.start();

    }
}



3 楼 daojin 2013-05-03  
有问题啊。你这个a如果一开始就notify过了。b就永远wait不到。
2 楼 王新春 2013-05-03  
           synchronized (self) {  
               synchronized (next) {  
                    System.out.println(name);  
                  count--;  
                    next.notify();  
              }  
               try {  
                   self.wait();  
              } catch (InterruptedException e) {  
                   e.printStackTrace();  
               }  
            }  


楼上也是正解,并且更简洁,我只是给个思路。我刚才查了下,这个题目网上很多答案,看样子以后还得多看看社区的题目啊
1 楼 terry21 2013-05-03  
public class Printer implements Runnable {
	private String name;
	private Object self;
	private Object next;

	private Printer(String name, Object self, Object next) {
		this.name = name;
		this.self = self;
		this.next = next;
	}

	public void run() {
		int count = 100;
		while (count > 0) {
			System.out.println(count);
			synchronized (self) {
				synchronized (next) {
					System.out.println(name);
					count--;
					next.notify();
				}
				try {
					self.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) throws Exception {
		Object a = new Object();
		Object b = new Object();
		Object c = new Object();

		Printer pa = new Printer("A", a, b);
		Printer pb = new Printer("B", b, c);
		Printer pc = new Printer("C", c, a);

		new Thread(pa).start();
		Thread.sleep(10);
		new Thread(pb).start();
		Thread.sleep(10);
		new Thread(pc).start();
		Thread.sleep(10);
	}
}

相关推荐

Global site tag (gtag.js) - Google Analytics