使用事件将信息传递给另一个线程,而不是同时执行

时间:2013-06-17 21:16:09

标签: java multithreading events

在下面的代码中,我有一个定期计时器,用于从在线队列中获取消息列表。此代码的目标是测试是否可以让重复计时器线程仅获取消息并将结果传递给主线程进行处理。

相反,我看到的是,尽管事件监听器位于主线程中,但在事件处理之后,在重复计时器线程中引发了所有在事件之后调用的操作,始终完成。我知道有可能偶尔会发生这种情况,这取决于任何给定时间cpu上的哪个线程,但我仍然应该看到print语句不时交织在一起。

我还通过向队列中添加大约50条消息来测试此效果,但我仍然看到相同的结果。

我的代码在

下面
public class Service implements NewWindowEventArgsListener
{

private final static String accessKey = 
        "secret";

private final static String secretKey = 
        "secret";

private final static String AWSSQSServiceUrl =
        "secret";


private boolean IsWindowing = false;
private ScheduledExecutorService _windowTimer;
private long SQSWindow = 60000;
private NewWindowEventArgs handler = new NewWindowEventArgs();
private static List<Message> messages = new ArrayList<Message>();


public void StartProcesses()
{
    if(this.IsWindowing)
    {
        return;
    }

    handler.addListener(this);

    this._windowTimer = Executors.newSingleThreadScheduledExecutor();

    Runnable task = new Runnable() {
        public void run() {
            WindowCallback();
        }
    };

    this._windowTimer.scheduleAtFixedRate(task,
            0, SQSWindow, TimeUnit.MILLISECONDS);

    IsWindowing = true;
}

private void WindowCallback()
{
    Date now = new Date();
    System.out.println("The service is running: " + now);

    int numberOfMessages = 0;
    ArrayList<String> attributes = new ArrayList<String>();
    AWSCredentials cred = new BasicAWSCredentials(accessKey, secretKey);
    ClientConfiguration config = new ClientConfiguration();
    config.setMaxErrorRetry(10);

    AmazonSQS client = new AmazonSQSClient(cred, config);

    client.setEndpoint(AWSSQSServiceUrl);

    System.out.println("Receiving messages from the Queue.\n");
    ReceiveMessageRequest receiveMessageRequest = 
            new ReceiveMessageRequest(AWSSQSServiceUrl);

    receiveMessageRequest.setMaxNumberOfMessages(10);

    GetQueueAttributesRequest numMessages = 
            new GetQueueAttributesRequest(AWSSQSServiceUrl); 

    attributes.add("ApproximateNumberOfMessages");
    numMessages.setAttributeNames(attributes);

    numberOfMessages = Integer.valueOf(
            (client.getQueueAttributes(numMessages)).getAttributes().
            get("ApproximateNumberOfMessages")).intValue();

    System.out.println("Expected number of Messages: " + numberOfMessages);

    do
    {
        messages.addAll(client.receiveMessage(receiveMessageRequest).
            getMessages());
    }while(messages.size() < numberOfMessages);

    System.out.println("Starting the printing of messages");

    if ( messages.size() > 0)
    {
        System.out.println("A message exists!");
        System.out.println();
        handler.NewWindowEvent(messages);
        System.out.println("//////////////////////////////////");
        System.out.println("\tEmptying message list");
        messages.clear();
        System.out.println("\tMessage list empty");
        System.out.println("//////////////////////////////////");
        System.out.println();
    }
}

@Override
public void JetstreamService_NewWindow(List<Message> messages) {
    System.out.println("Number of messages: " + messages.size() + "\n");

    ObjectMapper mapper = new ObjectMapper();

    try 
    {
        for (Message message : messages)
        {

            //System.out.println(message.getBody() + "\n");
            //byte[] bytes = DatatypeConverter.parseBase64Binary(message.getBody());

            //String messageBody = new String(bytes, "UTF-8");

            //System.out.println(messageBody + "\n");

            AmazonSNSMessage b;

            b = mapper.readValue(message.getBody(), AmazonSNSMessage.class);

            String subject = b.getSubject().trim().toLowerCase();
            System.out.println(subject);

            if (subject.equals("heartbeatevent"))
            {
                HeartbeatEvent heartbeat = new HeartbeatEvent();

                heartbeat.Deserialize(b.getMessage());

                System.out.println(heartbeat.getHeaderEventTime() + "\n");
            }

            else if(subject.equals("logicaldeviceaddedevent"))
            {
                LogicalDeviceAddedEvent logical = 
                        new LogicalDeviceAddedEvent();

                logical.Deserialize(b.getMessage());

                System.out.println(
                        logical.getLogicalDeviceAddedEventRegion() + "\n");
            }

            else if(subject.equals("logicaldeviceremovedevent"))
            {
                LogicalDeviceRemovedEvent logical = 
                        new LogicalDeviceRemovedEvent();

                logical.Deserialize(b.getMessage());

                System.out.println(
                        logical.getHeaderEventId());

                System.out.println(
                        logical.getHeaderEventTime() + "\n");
            }
        }
    } catch (JsonParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (JsonMappingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }   
}

有人可以解释为什么主线程中没有处理消息,或提供和解释为什么在处理完所有消息之后总是会发生明确消息打印语句。

1 个答案:

答案 0 :(得分:1)

我不确定我理解你不明白但我会试着澄清会发生什么:

  • 启动计时器,它开始等待下一个计划

  • 当执行定时器回调时选择一个线程:不是你的主线程在UI事件循环或等待或工作中忙,而是另一个线程;例如在.Net中,计时器实现使用来自全局线程池

  • 的线程
  • 所以你的回调,即WindowCallback,在这个线程同步上执行(似乎代码中没有异步)

  • 它到达 handler.NewWindowEvent(messages):这里我们必须区分两个正交概念:语义,引发一个事件,以及它的方式执行,就像任何方法调用一样,在同一个线程上,同步

  • 执行 JetstreamService_NewWindow ,处理消息并返回

  • 执行在handler.NewWindowEvent之后继续执行并清除消息列表

这是一个总结它的模式(ala UML):

O表示方法的开头

X结束

    timer  WindowCallback   NewWindowEvent  JetstreamService_NewWindow
    O
    |        
    +------->O
    |        |
    |        +---------------->O
    |        |                 |
    |        |                 +------------------>O
    |        |                 |                   |
    |        |                 +<------------------X
    |        |                 |
    |        +<----------------X
    |        |clear
    |<-------X
    X

嗯,我绝对不是艺术家:(

总结一下:从计时器开始,只有一个执行线程,它会一个接一个地同步运行你的所有方法。

希望这个帮助