我有一个小项目,我有2个帖子ListenerThread
和HeartbeatThread
,它们都是HeartbeatServer
内的嵌套类。
我所拥有的是当有人发送请求时,监听器线程将客户端添加到registerClients
(其余部分超出了此问题的范围)。
我正在寻找一种在HeartbeatThread
hashmap中没有客户端时暂停registerClients
的简单方法。
最初我认为它会像在ListenerThread
中使用while循环顶部的if语句一样容易,它会检查有多少客户端,如果客户端数量小于零ListenerThread
当客户端数量大约为零时,1}}会调用heartbeatServer.heartbeatThread.wait()
和heartbeatServer.heartbeatThread.notify()
。但是当我这样做时,java会抛出一个IllegalMonitorException
。在进行了一些挖掘后,我发现异常是因为我没有在wait()
块内调用synchronized
。
因为我期待只走一条路ListenerThread -> HeartbeatThread
而从来没有别的方法我怎么能做到这一点?在每个帖子中使用synchronized
块时,我还会更好吗?如果是这种情况,那么我需要澄清一下我正在同步的是什么。
我找到了这个例子;
boolean ready = false;
// thread 1
synchronized(lock) {
ready = true;
lock.notifyAll();
}
// thread 2
synchronized(lock) {
while(!ready)
lock.wait();
}
这个例子看起来会解决我的问题。但由于我是新手,我不确定lock
应该是什么。
可以重新设计或完成示例以满足我的需求,还是有更好的方法来解决我的问题?
我不确定需要显示哪些代码,以便发布所需的任何内容。
更新
我想我终于找到了解决问题的方法。 公共类HeartbeatServer {
final Object lock = new Object();
volatile Boolean suspended = true;
//TEST USE ONLY
volatile int userCount = 0;
// This is the thread that will send the heartbeats back to the client.
private class HeartbeatThread implements Runnable {
@Override
public void run() {
HeartbeatServer heartbeatServer = HeartbeatServer.getInstance();
while (true) {
synchronized (heartbeatServer.lock) {
if(suspended) {
try {
System.out.println("Suspending heartbeat thread!");
heartbeatServer.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("Resuming heartbeat thread!");
} // End if block
} // End synchronized block
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
} // End while loop
} // End run method
} // End HeartbeatThread
// This is the thread that will listen for clients connecting to the server.
private class ListenerThread implements Runnable {
// The instance of heartbeatServer should be the first object so we can use it everywhere in the class.
HeartbeatServer heartbeatServer = HeartbeatServer.getInstance();
private void suspendHeartbeats() {
synchronized (heartbeatServer.lock) {
heartbeatServer.suspended = true;
}
} // End suspendHeartbeats
private void resumeHeartbeats() {
synchronized (heartbeatServer.lock) {
heartbeatServer.suspended = false;
heartbeatServer.lock.notify();
}
} // End resumeHeartbeats
@Override
public void run() {
while(true) {
if(heartbeatServer.userCount < 1) {
suspendHeartbeats();
} else {
resumeHeartbeats();
}
} // End while loop
} // End run method
} // End ListenerThread
} // End HeartbeatServer
我还能做些什么来改善我的线程吗? HeartbeatThread中的一些部分是在测试中遗留下来的。但我更多地提到用于暂停HeartbeatThread的逻辑。
答案 0 :(得分:2)
你听起来已经找到了你需要的核心,只是信心阻挡你。 wait / notify是Java中相当低级的原语,并且在使用它们时会出现一个微妙的时序错误。这就是为什么存在像ReadWriteLock,Semaphore等更高级别的抽象的原因。 Java API文档中有一个示例演示了如何阻止直到达到条件(例如空或满)here,他们使用ReentrantLock和Condition来表示空/非空。
回到你关于锁对象的问题,它只是他们正在调用wait / notify的对象。它可以或多或少是任何对象,只要它始终是同一个对象。
有一个论点说当使用synchronized / wait / notify时,应该使用私有的最终对象而不是公开的对象。参数是,如果锁定对象被暴露,那么其他代码本身可以调用wait / notify / synchronized来对其进行调整并产生副作用,这可能会变得难以追踪。无论如何,线程代码很难,所以这个论点的目的是减少外部干扰蔓延的机会。这解释了你的例子,因为它符合这个惯例。