我正在使用Java的多客户端TCP服务器。由于服务器必须能够执行大量的同步操作,我创建了一个线程,用于通过套接字将数据发送到我的客户端。它的定义如下:
public class MessageSender implements Runnable{
private OutputStream output;
private volatile ArrayList<byte[]> messagesToSend;
public MessageSender(OutputStream _output)
{
output = _output;
messagesToSend = new ArrayList<byte[]>(0);
}
public void run()
{
while (true)
{
if (messagesToSend.size() > 0)
{
sendMessage(messagesToSend.get(0));
messagesToSend.remove(0);
}
}
}
public void setMessageToSend(Message message)
{
messagesToSend.add(message.getMessageHandler().getBytes());
}
public void sendMessage(byte[] bytes)
{
if (bytes == null)
{
System.out.println("bytes = null");
return ;
}
try
{
output.write(bytes, 0, bytes.length);
}
catch (IOException e)
{
System.out.println(e);
}
}
}
您可以在Message
中看到的课程setMessageToSend()
是我的一类。我还创建了其他类(Channel_test
,Channel_status
),对应于特定消息,并继承自Message
类。
我有另一个帖子,我在其中创建Channel_status
,如下所示:
eventBus.consumer(msg + port , message -> {
sender.setMessageToSend(new Channel_status(eParameters));
channel_test = new Channel_test(eParameters);
System.out.println("Created Message");
});
这是一个Vertx
处理程序,只在从客户端收到特定消息时调用一次。它被调用,并且channel_status按预期进行了实例化。在同一个线程中,我有一个Vertx计时器,我将我的channel_status类包含的字节数组发送到MessageSender
。
vertx.setPeriodic(duration, id -> {
System.out.println(Arrays.toString(channel_test.getMessageHandler().getBytes()));
sender.setMessageToSend(channel_test);
});
正如您所看到的,我正在打印它的值以确保它不为空。并且,它在计时器中永远不会为空。但是当MessageSender收到它时,有时它是null(有时我的意思是大约1/50倍)。为什么?这是一个并发问题吗?
感谢。
答案 0 :(得分:2)
使ArrayList<byte[]>
volatile
does not保证在thread1中添加新元素时,thread2可以看到它。
您应该使用支持concurrency的列表,例如:
messagesToSend = Collections.synchronizedList(new ArrayList<byte[]>(0));
这可以确保当您按setMessageToSend
添加新元素时,新元素将在run
方法中显示。
Collections.synchronizedList
仍无法保证线程安全。因为在run
方法中,这三个操作不是原子的。
if (messagesToSend.size() > 0)
{
sendMessage(messagesToSend.get(0));
messagesToSend.remove(0);
}
BlockingQueue
可以解决此问题:
private BlockingQueue<byte[]> messagesToSend;
public MessageSender(OutputStream _output) {
output = _output;
messagesToSend = new LinkedBlockingQueue<>();
}
public void run() {
while (true) {
try {
sendMessage(messagesToSend.take()); // this will get blocked untill the queue is not empty
} catch (InterruptedException e) {
}
}
}
public void setMessageToSend(Message message) {
try {
messagesToSend.put(message.getMessageHandler().getBytes()); // put new element in the queue, check if run method is blocked, wake it up
} catch (InterruptedException e) {
}
}
答案 1 :(得分:0)
将您的.alphabet {
width: 70px;
}
方法更改为
run
}
public void run() {
while (true) {
synchronized(messagesToSend) {
while (messagesToSend.isEmpty()) {
messagesToSend.wait();
}
}
sendMessage(messagesToSend.get(0));
messagesToSend.remove(0);
}
为
setMessageToSend()