以上代码仅是框架,增加几处细节可使代码更加完善。
首先增加异常处理。一个连接尝试的结果可能是异常而不是JMXConnector。虽然并不改变代码的逻辑,但增加了代码的复杂性。
主线程调用BlockingQueue.poll则抛出异常InterruptedException,代码必须处理这异常。
其次是在完成连接后清除连接线程。框架代码没有对ExecutorService调用shutdown()方法,所以每次调用connectWithTimeout时,都生成一个新的单线程执行器对象及其线程。垃圾收集器可能会会受这些执行器对象和线程,但不该依赖于这种可能性的因素。
关于线程还有些微妙之处,框架中的代码会生成一些非守护线程。主线程结束时,因为还有未结束的非守护线程所以应用程序还不能结束。如果有一个线程还在尝试连接而应用程序却结束运行,那个线程就会一直残留到连接尝试超时为止,这一点正是我们要竭力避免的。因此我们的目标变成创建一个守护线程。
详细代码如下
public static JMXConnector connectWithTimeout(
final JMXServiceURL url, long timeout, TimeUnit unit)
throws IOException ...{
final BlockingQueue<Object> mailbox = new ArrayBlockingQueue<Object>(1);
ExecutorService executor =
Executors.newSingleThreadExecutor(daemonThreadFactory);
executor.submit(new Runnable() ...{
public void run() ...{
try ...{
JMXConnector connector = JMXConnectorFactory.connect(url);
if (!mailbox.offer(connector))
connector.close();
} catch (Throwable t) ...{
mailbox.offer(t);
}
}
});
Object result;
对象结果:
try ...{
result = mailbox.poll(timeout, unit);
if (result == null) ...{
if (!mailbox.offer(""))
result = mailbox.take();
}
} catch (InterruptedException e) ...{
throw initCause(new InterruptedIOException(e.getMessage()), e);
} finally ...{
executor.shutdown();
}
if (result == null)
throw new SocketTimeoutException("Connect timed out: " + url);
if (result instanceof JMXConnector)
return (JMXConnector) result;
try ...{
throw (Throwable) result;
} catch (IOException e) ...{
throw e;
} catch (RuntimeException e) ...{
throw e;
} catch (Error e) ...{
throw e;
} catch (Throwable e) ...{
// In principle this can't happen but we wrap it anyway
throw new IOException(e.toString(), e);
}
}
![]()
private static <T extends Throwable> T initCause(T wrapper, Throwable wrapped) ...{
wrapper.initCause(wrapped);
return wrapper;
}
![]()
private static class DaemonThreadFactory implements ThreadFactory ...{
public Thread newThread(Runnable r) ...{
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
}
}
private static final ThreadFactory daemonThreadFactory = new DaemonThreadFactory();
initCause方法虽然只使用了一次,但对于处理没有可抛出参数的那些异常来说却很方便。如果java.util.concurrent包中提供守护线程工厂类DaemonThreadFactory,就不需要每个程序员重新发明轮子了。
代码改进
以上的代码并不能令人十分满意。在用户取消或中断等干扰的情况下,会出现一些特殊的边界条件:已经返回连接结果,而任务却取消了,这样就发生了JMXConnector的泄漏;或者在主线程将要返回的时候,去关闭JMXConnector。
结论
以上介绍了在完全不必考虑底层Socket机制的条件下,如何在创建远程连接时设置超时。这种技术在很多场合下是颇有实用价值的,并且不只局限于JMX 远程API,访问web服务或EJB等场合也可采用。
