我有一个小项目,其中有2个线程ListenerThread
和HeartbeatThread
都是HeartbeatServer
中的嵌套类。
我所拥有的是,当有人发送请求时,侦听器线程会将客户端添加到registerClients
(其余内容不在此问题的范围之内)。
当HeartbeatThread
哈希图中没有客户端时,我正在寻找一种挂起registerClients
的简单方法。
最初我想这很容易,就像在ListenerThread
的while循环顶部使用if语句来检查有多少个客户端,如果客户端计数小于零,则ListenerThread
会调用和heartbeatServer.heartbeatThread.wait()
(当客户计数约为零时)。但是,当我这样做时,java会抛出一个heartbeatServer.heartbeatThread.notify()
。经过一些挖掘后,我发现了异常是因为我没有在IllegalMonitorException
块内调用wait()
。
既然我只想做一种方法synchronized
,而从不做别的方法,我该怎么做呢?在每个线程中使用ListenerThread -> HeartbeatThread
块还是更好吗?如果是这种情况,那么我需要澄清一下我正在同步的内容。
我找到了这个例子。
boolean ready = false;
// thread 1
synchronized(lock) {
ready = true;
lock.notifyAll();
}
// thread 2
synchronized(lock) {
while(!ready)
lock.wait();
}
这个例子看起来可以解决我的问题。但是由于我是新手,所以我不确定
synchronized
应该是什么。是否可以根据我的需要对示例进行重新设计或完成,或者有更好的方法来解决我的问题?
我不确定要显示什么代码,所以我可以张贴任何要求的内容。
更新
我想我终于解决了我的问题。
公共类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的逻辑。
最佳答案
您听起来好像已经找到了所需的核心,这只是信心使您退缩了。在Java中,wait / notify是相当低级的原语,在使用它们时通常会潜伏着细微的计时错误。这就是为什么存在诸如ReadWriteLock,Semaphore等更高级别的抽象的原因。 Java API文档中有一个示例演示如何阻塞直到达到条件(例如空或满)here,他们已经使用ReentrantLock和Condition来指示空/不空。
回到关于锁对象的问题,这只是他们正在调用等待/通知的对象。只要它始终是同一对象,它就可以是或多或少的任何对象。
有一种说法认为,在使用synced / wait / notify时,应使用私有的最终对象而不是公开的对象。论据是,如果锁定对象被暴露,则其他代码本身可能会对其调用wait / notify / synchronized,并引起副作用,而这种副作用可能很难跟踪。无论如何,线程代码都是很难的,因此该论据旨在减少外部干扰蔓延的机会。这是对您的示例的解释,因为它遵循了这一约定。