簡體   English   中英

Java ExecutorService線程寫入同一字段

[英]Java ExecutorService threads writing to same field

首先在這里發布,大家好。 我正在構建一個小型客戶端,該客戶端需要通過套接字發送心跳並通過套接字接收各種固定長度的二進制消息。 我有一個連接管理器類,它啟動套接字連接並創建2個線程,一個線程每x秒發送一次心跳。 另一個線程監聽輸入。

收到輸入后,它將報頭讀入字節數組(始終為4個字節),它得到msg長度x = byte [3],然后將接下來的x個字節讀入消息字節數組。 然后,它使用ExecutorService創建一個新的消息處理程序對象,該對象將接收消息字節數組。

消息處理器實現可運行並打印出消息字節。 但是,如果套接字接收到大量數據,即服務器按順序發送消息,那么我的消息處理器似乎會從不同的線程中打印出混合數據-我認為我的執行者將創建一個新的對象,該對象是線程安全的,並且具有自己的binary []實例。 msgBytes。

connectionManager在其下面創建發送/ rcv線程,rcv線程創建線程池,並在讀取消息時創建新的messageProcessor對象以處理byte []消息

public class connectionManager extends Thread {
    public connectionManager(InetAddress host, int serverSocket) {
        System.out.println("in connectionManager create");
        try {
            clientSocket = new Socket(host, serverSocket);
            outToServer = new DataOutputStream(clientSocket.getOutputStream());
            connected = true;
            final ExecutorService executor = Executors.newFixedThreadPool(10);

            final Thread inThread = new Thread() {
                @Override 
                public void run() {
                    while (connected) {
                        try {
                            DataInputStream dIn = new DataInputStream(clientSocket.getInputStream());

                            byte[] header = new byte[4];
                            dIn.readFully(header); // read the message                            

                            String msgType = new String(new byte[]{ header[3] }, "US-ASCII");

                            short length = getShortFromLittleEndianRange(header, 1); //test function to return length offset is start position of length   
                            byte[] message = new byte[length - 1]; //minus the msg type byte

                            dIn.readFully(message);

                            executor.execute(new MessageProcessor(message, msgType));
                            } catch (Exception e) {
                                e.printStackTrace();
                                connected = false;
                            } 
                    }
                    executor.shutdown();
                    System.out.println("Shutdown executor");
        };
            };
            inThread.start();          


            final Thread outThread = new Thread() {
            @Override
                public void run() {
                    Heartbeat hb = new Heartbeat();
                    while(connected) {
                        PrintWriter out = null;
                        try {
                            this.sleep(3000);                           
                            outToServer.write(hb.serialize());
                        } catch (Exception e) {
                            e.printStackTrace();
                            System.out.println("cant send heartbeat server not alive...");
                        }
                    }
        };
            };
            outThread.start();            
        } catch (Exception e) {
            System.out.println("Cannot connect to server");
            System.exit(0);
        }
    }    
}

MessageProcess實現可運行,我希望執行程序創建新的MessageProcess對象,每個對象都具有自己的msgBytes和msgType實例

public class MessageProcessor implements Runnable {

    private byte[] msgBytes;
    private String msgType;

    MessageProcessor(byte[] newMsgBytes, String newMsgType) {
    msgBytes = newMsgBytes; 
    msgType = newMsgType;
    }    

    @Override
    public void run() {
    output();
    }

    synchronized void output() {
        System.out.println("\nMessageProcessor process inbound message " + Thread.currentThread().getId());
        System.out.println("message type : " + " " + msgType);
        for(byte b : msgBytes){
            System.out.printf("%02X",b);
        }           
        System.out.println("\nfinished MessageProcessor " + Thread.currentThread().getId());        
    }
}

但是,當我運行並從服務器接收“大量”的msg消息時,consol輸出看起來好像存在線程安全問題:下面的示例輸出。

MessageProcessor process inbound message 16
message type : 8

01D40806004530515266385779624C6D653830
MessageProcessor process inbound message 17
2D36667664374E6D6A4100000000000000003030515266444E31417A766846000000000000000000000000020000000000A90DAE0400000001000000000000000000000000FA10000000000100000000000000004C43484C4742324500000052D7048024E3F70400BCBDE256306F0100000203003030515266444E31417A766800000000message type : 8


finished MessageProcessor 16
01D50806004530515266385779624C6D6638302D366676643176584A5200000000000000003030515266444E31417A766246000000000000000000000000010000000000A90DAE0400000001000000090000000109000000FA10000000000200000000000000004C43484C4742324500000041D7048024E3F70400BCBDE256306F0100000003003030515266444E31417A766200000000
finished MessageProcessor 17

我希望看到

MessageProcessor process inbound message 16
message type : 8
<hex>

finished MessageProcessor 16

MessageProcessor process inbound message 17
message type : 8
<hex>

finished MessageProcessor 17

我在做自己的事情不安全嗎?

非常感謝Matt

由於您使用的是MessageProcessor不同實例(每條消息一個),因此output()上的synchronized標簽不會產生我認為您期望的效果。 每個實例將能夠訪問其自己的output()而不被其他實例阻塞。

您看到的輸出是來自不同實例的輸出的混合。 為了防止混淆,您可以使用StringBuilder構造輸出,然后在一個調用中將其提交給System.out 如果要跨實例同步輸出,請考慮使用隊列。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM