题目:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着又回到主线程又循环100次,如此循环50次。写出程序。
1、思路:
这是一道考察多线程的题目。应题目的要求,子线程循环10次和主线程循环100次的中途不能被打断,则需要在循环方法里面加入互斥锁。要连续有规律运行主线程和子线程,可以考虑当线程执行完一个周期的循环后,让线程休眠让出线程运行权,或者使用wita()和notify()方法。
2、答案之一:
public class TranditionThreadCommunication {
public static void main(String[] args) {
final Business business = new Business();
// 子线程循环
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
try {
business.sub(i);
} catch (InterruptedException e) {
}
}
}
}).start();
// 主线程循环
for (int i = 1; i <= 50; i++) {
try {
business.mian(i);
} catch (InterruptedException e) {
}
}
}
}
/**
* 业务类型(包含各色的同步锁)
*/
class Business {
// sub()方法是否该运行标识
private boolean bShouldSub = true;
/**
* 循环100次打印的方法sub()
*
* @param i
* @throws InterruptedException
*/
public synchronized void sub(int i) throws InterruptedException {
while (!bShouldSub) { // 当 bShouldSub 为 false 时,则等待
this.wait();
}
for (int j = 1; j <= 10; j++) {
System.out.println("sub thread : 第" + i + "行, 第" + j + "列");
}
bShouldSub = false; // 执行for循环后,标志sub()方法不可再执行
this.notify(); // 唤醒线程
}
/**
* 循环100次打印的方法mian()
*
* @param i
* @throws InterruptedException
*/
public synchronized void mian(int i) throws InterruptedException {
while (bShouldSub) {
this.wait();
}
for (int j = 1; j <= 100; j++) {
System.out.println("main thread : 第" + i + "行, 第" + j + "列");
}
bShouldSub = true; // 执行for循环后,标志sub()方法可再执行了
this.notify(); // 唤醒线程
}
}
3、经验:
(1)要用到共同数据(包括同步锁)或共同算法的若干个方法应该归在同一个类身上,这种设计体现了高类聚和程序的健壮性。像上面的循环10次和循环100次的操作都需要用到锁时,可以将这两个方法加上锁(synchronized)并放到Business类中。
(2)上面答案中的
while (bShouldSub) {
this.wait();
}
借鉴了javaAPI 中的wait()方法的写法,主要是为了防止线程之间可能出现的伪唤醒。
java API 中的wait()方法的原文如下:
public final void wait() throws InterruptedException 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。 当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。
对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用:
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
// Perform action appropriate to condition
}
此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅 notify 方法。 抛出: IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。 InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态 被清除。