来自一个线程的非null值,在另一个线程中为null

时间:2018-05-02 14:45:35

标签: java multithreading vert.x blockingqueue

我正在使用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_testChannel_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倍)。为什么?这是一个并发问题吗?

感谢。

2 个答案:

答案 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()