在我的主线程中,我创建并启动四个线程(A,B,C,D
),每隔500到1000毫秒在控制台上打印字母和数字。
例如A1,A2,A3
等
主线程假设每100ms暂停一次随机Letter
线程然后将其唤醒。 2秒后,它会全部杀死它们。
我的问题是我无法暂停随机Letter
线程然后将其唤醒,因为我得到:IllegalMonitorStateException
我的主要线程类:
public class Main extends Thread {
private boolean alive;
private ArrayList<Letter> letters;
private Letter toStop;
public static Object mutex;
public Main() {
letters = new ArrayList<Letter>();
alive = true;
mutex = new Object();
}
public void run() {
try {
Timer timer = new Timer();
timer.schedule(new StopTask(timer, this), 2 * 1000);
letters.add(new Letter());
letters.add(new Letter());
letters.add(new Letter());
letters.add(new Letter());
for (Letter letter : letters) {
new Thread(letter).start();
}
while (alive) {
synchronized (mutex) {
toStop = letters.get((int) (Math.random() * letters.size()));
System.out.println(toStop.getLetter() + " spi");
mutex.wait();
Thread.sleep(100);
mutex.notify();
}
for (Letter letter : letters) {
letter.kill();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void kill() {
alive = false;
}
}
和我的Letter
班级:
class Letter implements Runnable {
private static int ID;
private char letter;
private int counter;
private boolean alive;
public Letter() {
letter = (char) ('A' + ID);
alive = true;
ID++;
}
@Override
public void run() {
try {
while (alive) {
System.out.println(letter + "" + counter);
counter++;
Thread.sleep((int) (Math.random() * 501 + 500));
}
System.out.println("Watek " + letter + " sie zakonczyl");
} catch (Exception e) {
}
}
public void kill() {
alive = false;
}
public char getLetter() {
return letter;
}
}
StopTask
:
import java.util.Timer;
import java.util.TimerTask;
public class StopTask extends TimerTask {
private Timer timer;
private Main main;
public StopTask(Timer timer, Main main) {
this.timer = timer;
this.main = main;
}
public void run() {
System.out.println("Time's up!");
main.kill();
timer.cancel(); //Not necessary because we call System.exit
}
}
答案 0 :(得分:0)
您的代码示例不起作用,因为wait()调用是在没有拥有对象监视器的情况下进行的。
这是javadoc的解释: https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait()
以下是javadoc的片段:
public final void wait() 抛出InterruptedException
使当前线程等待,直到另一个线程为此对象调用notify()方法或notifyAll()方法。换句话说,此方法的行为就像它只是执行调用wait(0)一样。 当前线程必须拥有此对象的监视器。线程释放此监视器的所有权并等待,直到另一个线程通过调用notify方法或notifyAll方法通知等待此对象监视器的线程唤醒。然后该线程等待,直到它可以重新获得监视器的所有权并继续执行。
在一个参数版本中,中断和虚假唤醒是可能的,并且此方法应始终在循环中使用:
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
此方法只能由作为此对象监视器所有者的线程调用。有关线程可以成为监视器所有者的方式的说明,请参阅notify方法。 抛出: IllegalMonitorStateException - 如果当前线程不是对象监视器的所有者。 InterruptedException - 如果任何线程在当前线程等待通知之前或当前线程中断当前线程。抛出此异常时,将清除当前线程的中断状态。
我会重新设计代码以使线程等待,而不是被告知从外面等待。例如,使用一些共享对象来共享线程之间的状态。 我也会使用预定的线程执行程序。让生活更轻松: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html
答案 1 :(得分:0)
这是一个典型的生产者 - 消费者问题,有更好的方法来做到这一点。由于我们正在解决手头的问题,你可以在下面做。
您可以摆脱互斥锁对象并使用Letter
实例作为互斥锁,因此您有效地拥有4个互斥锁,每个互斥锁与Main
协调
public class Main extends Thread{
private boolean alive;
private ArrayList<Letter> letters;
private Letter toStop;
//public static Object mutex;
public Main() {
letters = new ArrayList<Letter>();
alive = true;
mutex = new Object();
}
public void run() {
try {
Timer timer = new Timer();
timer.schedule(new StopTask(timer, this), 2 * 1000);
letters.add(new Letter());
letters.add(new Letter());
letters.add(new Letter());
letters.add(new Letter());
for (Letter letter : letters) {
new Thread(letter).start();
}
while (alive) {
// synchronized (mutex) {
toStop = letters.get((int) (Math.random() * letters.size()));
synchronized (toStop) {
//System.out.println(toStop.getLetter() + " spi");
// mutex.wait();
//Thread.sleep(100);
// mutex.notify();
toStop.setToGetLetter(true);
toStop.notify();
}
System.out.println(toStop.getLetter() + " spi");
Thread.sleep(100);
}
// }
for (Letter letter : letters) {
letter.kill();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void kill() {
alive = false;
}
}
在您的Letter
话题中,您可以使用this
与主协调
public class Letter implements Runnable {
private static int ID;
private char letter;
private int counter;
private boolean alive;
private volatile boolean toGetLetter = false;
public boolean isToGetLetter() {
return toGetLetter;
}
public void setToGetLetter(boolean toGetLetter) {
this.toGetLetter = toGetLetter;
}
public Letter() {
letter = (char) ('A' + ID);
alive = true;
ID++;
}
@Override
public void run() {
try {
while (alive) {
synchronized (this) {
while (!isToGetLetter()) {
this.wait();
}
System.out.println(letter + "" + counter);
counter++;
Thread.sleep((int) (Math.random() * 501 + 500));
toGetLetter = false;
this.notify();
}
}
System.out.println("Watek " + letter + " sie zakonczyl");
} catch (Exception e) {
}
}
public void kill() {
alive = false;
}
public char getLetter() {
return letter;
}
}