我正在研究与Java相关的实际场景;套接字程序。现有系统和预期系统如下。
现有系统 - 系统检查是否满足某个条件。如果是这样它将创建一些要发送的消息并将其放入队列中。
队列处理器是一个单独的线程。它会定期检查队列中是否存在项目。如果找到任何项目(消息),它只是将消息发送到远程主机(硬编码)并从队列中删除该项目。
预期系统 - 就是这样的。在满足特定条件时创建消息,但在每种情况下接收者都不相同。所以有很多方法。
将消息放入同一队列但使用其接收者ID。在这种情况下,第二个线程可以识别接收器,以便可以将消息发送到该接收器。
拥有多个主题。在这种情况下,当满足条件并且接收器处于“新”状态时,它会创建一个新队列并将消息放入该队列。并且新线程初始化以处理该队列。如果下一个消息被定向到同一个接收者,它应该放在同一个队列中,如果不是新队列,则应该创建该线程。
现在我想实施第二个,有点卡住了。我该怎么办?一个骨架就足够了,您不必担心如何创建队列等......:)
更新:我也认为方法1是最好的方法。我读了一些关于线程的文章并做出了这个决定。但是,了解如何实施方法2也是非常值得的。
答案 0 :(得分:4)
考虑使用Java Message Services (JMS)而不是重新发明轮子?
答案 1 :(得分:2)
我可以建议您查看BlockingQueue吗?您的调度进程可以写入此队列(put),客户端可以采用线程安全方式。所以你根本不需要编写队列实现。
如果你有一个包含不同消息类型的队列,那么你需要为每个客户端实现一些peek-type机制(即他们必须检查队列的头部并且只采用他们的那些)。为了有效地工作,消费者必须及时,有力地提取他们所需的数据。
如果每个消息/消费者类型有一个队列/线程,那么这将更容易/更可靠。
您的客户端实现只需循环:
while (!done) {
Object item = queue.take();
// process item
}
请注意,队列可以使用泛型,take()
正在阻止。
当然,如果有多个消费者收集不同类型的消息,您可能需要考虑space-based architecture。这不具有队列(FIFO)特性,但可以非常简单地为您提供多个消费者。
答案 2 :(得分:2)
无论你是否拥有大量终端机器和偶尔发送的消息,或者有几台终端机器以及频繁发送消息,你都需要稍微权衡。
如果你有很多终端机器,那么每台终端机器上只有一个线程听起来有点过头,除非你真的要不断地向所有这些机器传输消息。我建议有一个线程池,它只会在某些边界之间增长。为此,您可以使用ThreadPoolExecutor。当您需要发布消息时,实际上是向执行者提交了一个runnable,它将发送消息:
Executor msgExec = new ThreadPoolExecutor(...);
public void sendMessage(final String machineId, byte[] message) {
msgExec.execute(new Runnable() {
public void run() {
sendMessageNow(machineId, message);
}
});
}
private void sendMessageNow(String machineId, byte[] message) {
// open connection to machine and send message, thinking
// about the case of two simultaneous messages to a machine,
// and whether you want to cache connections.
}
如果您只有几台终端机器,那么每台机器可能有一个BlockingQueue,并且每个阻塞队列的线程都在等待下一条消息。在这种情况下,模式更像是这样(谨防未经测试的头顶星期日早上代码):
ConcurrentHashMap<String,BockingQueue> queuePerMachine;
public void sendMessage(String machineId, byte[] message) {
BockingQueue<Message> q = queuePerMachine.get(machineId);
if (q == null) {
q = new BockingQueue<Message>();
BockingQueue<Message> prev = queuePerMachine.putIfAbsent(machineId, q);
if (prev != null) {
q = prev;
} else {
(new QueueProessor(q)).start();
}
}
q.put(new Message(message));
}
private class QueueProessor extends Thread {
private final BockingQueue<Message> q;
QueueProessor(BockingQueue<Message> q) {
this.q = q;
}
public void run() {
Socket s = null;
for (;;) {
boolean needTimeOut = (s != null);
Message m = needTimeOut ?
q.poll(60000, TimeUnit.MILLISECOND) :
q.take();
if (m == null) {
if (s != null)
// close s and null
} else {
if (s == null) {
// open s
}
// send message down s
}
}
// add appropriate error handling and finally
}
}
在这种情况下,如果该机器的消息在60秒内没有到达,我们将关闭连接。
您应该使用JMS吗?好吧,你必须权衡这对你来说是否复杂。我个人的感觉是,保证一个特殊的框架并不是一项复杂的任务。但我确信意见不同。
P.S。实际上,现在我看一下这个,你可能把队列放在线程对象中,只是映射机器ID - &gt;线程对象。无论如何,你明白了。
答案 3 :(得分:2)
您可以尝试使用SomnifugiJMS,这是一个使用java.util.concurrent作为实际“引擎”的in-vm JMS实现。
对于您的目的而言可能有点过分,但很可能使您的应用程序几乎不需要额外的编程(如果适用),您只需插入一个不同的JMS实现,如ActiveMQ和你'重做。
答案 4 :(得分:1)
首先,如果您计划拥有大量接收器,我不会使用ONE-THREAD-AND-QUEUE-PER-RECEIVER方法。你最终可能会遇到大量没有做任何事情的线程,我可能会伤害到你的性能。另一种方法是使用工作线程的线程池,只从共享队列中挑选任务,每个任务都有自己的接收器ID,也许还有一个共享字典,每个接收器都有套接字连接,供工作线程使用。
话虽如此,如果你仍然想要追求自己的方法,你可以做的是:
1)创建一个新类来处理新的线程执行:
public class Worker implements Runnable {
private Queue<String> myQueue = new Queue<String>();
public void run()
{
while (true) {
string messageToProcess = null;
synchronized (myQueue) {
if (!myQueue.empty()) {
// get your data from queue
messageToProcess = myQueue.pop();
}
}
if (messageToProcess != null) {
// do your stuff
}
Thread.sleep(500); // to avoid spinning
}
}
public void queueMessage(String message)
{
synchronized(myQueue) {
myQueue.add(message);
}
}
}
2)在主线程上,创建消息并使用字典(哈希表)来查看接收者的线程是否已经创建。如果是,则只是将新消息排队。如果没有,请创建一个新线程,将其放入哈希表并将新消息排队:
while (true) {
String msg = getNewCreatedMessage(); // you get your messages from here
int id = getNewCreatedMessageId(); // you get your rec's id from here
Worker w = myHash(id);
if (w == null) { // create new Worker thread
w = new Worker();
new Thread(w).start();
}
w.queueMessage(msg);
}
祝你好运。
编辑:您可以使用此方法中提到的BlockingQueue Brian来改进此解决方案。