2013-04-26 62 views
0

我編程網絡單獨的類,它需要在一個線程中運行,到目前爲止,還沒有問題,但是我不能讓它黯然工作。java的單網絡實現Runnable

網絡類:

public class Network extends Thread {  
    private static Network cachedInstance = new Network(); 

    private PrintWriter out; 
    private BufferedReader in; 

    private Network() {   
    } 

    private void init() { 
     try { 
      Socket clientSocket = new Socket(Config.HOST_NAME, Config.HOST_PORT); 
      out = new PrintWriter(clientSocket.getOutputStream(), true); 
      in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 

      String fromServer; 
      while ((fromServer = in.readLine()) != null) { 
       System.out.println("Server: " + fromServer); 
      } 

     } catch (IOException ex) { 
      Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    public static Network getInstance() { 
     return cachedInstance; 
    } 

    public void send(final String string) { 
     out.println(string); 
    } 

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

控制器類的一部分:

public void clientTest() { 
    int random = new Random().nextInt(1000); 
    Network.getInstance().start(); 
    Network.getInstance().send(random + ""); 
} 

我得到的錯誤:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException 
at network.Network.send(Network.java:52) 
at controller.Controller.clientTest(Controller.java:126) 

所以它看起來像單一網絡實例不正確實例化,理論上應該是不可能的。

第二個問題我有是,如果我能避免使用這樣的:

Network.getInstance().start(); 

換句話說,我想,以確保只有一個線程(網絡類)創建,它總是被默認運行當這些類被初始化時。目前這種方式並不差,但我認爲它會更好。

對於人們想知道爲什麼我用這個方法:基本上,我只是想用Network.send()發送到一個固定的服務器。那個服務器當然可以發回東西,但是在那之後,網絡需要在某個時刻從Controller響應和調用方法。

問候。

編輯:建議的解決方案,基於反應

Network.class:

public class Network implements Runnable {  
    private static final Network cachedInstance; 
    static { 
     Network tempInstance = null; 
     try { 
      tempInstance = new Network(Config.HOST_NAME, Config.HOST_PORT); 
     } catch (IOException ex) { 
      Logger.getLogger(Network.class.getName()).log(Level.SEVERE, null, ex); 
     } finally { 
      cachedInstance = tempInstance; 
     } 
    } 

    private final Socket clientSocket; 
    private final PrintWriter out; 
    private final BufferedReader in; 

    private Network(final String hostname, final int port) throws IOException { 
     clientSocket = new Socket(hostname, port); 
     out = new PrintWriter(clientSocket.getOutputStream(), true);   
     in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
    } 

    public static Network getInstance() { 
     return cachedInstance; 
    } 

    @Override 
    public void run() { 
     try { 
      String fromServer; 
      while ((fromServer = in.readLine()) != null) { 
       System.out.println("Server: " + fromServer); 
      } 
     } catch (IOException ex) { 
      Logger.getLogger(Network.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    public void send(final String string) { 
     out.println(string); 
    } 

    public void close() { 
     try { 
      in.close(); 
      out.close(); 
      clientSocket.close(); 
     } catch (IOException ex) { 
      Logger.getLogger(Network.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 
} 

調用代碼:

public void clientTest() { 
    int random = new Random().nextInt(1000); 
    Network network = Network.getInstance(); 
    new Thread(network).start(); 
    network.send(random + ""); 
    network.close(); 
} 

這只是用於測試,在現實中的連接需求在用戶關閉程序之前保持打開狀態。

+0

您正在設置「out」之前調用send。順便說一句你需要將字段'out'設置爲'volatile'字段,否則你可能永遠不會看到它被設置。 – 2013-04-26 22:00:00

回答

1

一個簡單的解決問題的辦法是讓你啓動線程之前,所以你沒有這種競爭情況擔心連接建立。

public class Network implements Runnable, Closeable {  
    private final Socket clientSocket; 
    private final PrintWriter out; 
    private final BufferedReader in; 
    private volatile boolean closed = false; 

    public Network(String hostname, int port) throws IOException {   
     clientSocket = new Socket(hostname, port); 
     out = new PrintWriter(clientSocket.getOutputStream(), true); 
     in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
    } 

    public void run() { 
     try { 
      for(String fromServer; (fromServer = in.readLine()) != null;) 
       System.out.println("Server: " + fromServer); 
     } catch (IOException ex) { 
      if (!closed) 
       Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    public void send(String line) { 
     out.println(line); 
    } 

    public void close() { 
     closed = true; 
     try { clientSocket.close(); } catch (IOException ignored) { } 
    } 
} 

爲了測試

@Test 
public void testClient() { 
    Network network = new Network(Config.HOSTNAME, Config.PORT) 
    new Thread(network).start(); 

    int random = new Random().nextInt(1000); 
    network.send(random + ""); 
    network.close(); 
} 

注意:在使用狀態的單身類使單元測試很困難,因爲你必須單身重置回其初始狀態或一個測試可能會影響另一個和你運行的順序可以有所作爲。在上面的測試中,它是獨立的,沒有其他測試受到傷害。 ;)

+0

非常感謝!事實上,我已經對這個問題放棄了希望。我現在會測試它,但它肯定會工作。 – skiwi 2013-04-27 10:42:25

+0

雖然有一個問題:爲什麼構造函數拋出IOException?這不意味着這段代碼的調用者需要做異常處理嗎?在我看來,這是令人討厭的,因爲班級本身(網絡在這裏)也可以做到這一點? – skiwi 2013-04-27 10:45:55

+0

它可以做異常處理,但通常只有調用者知道應該做什麼。無論如何,你不想創建一個實際上沒有連接任何東西的網絡。即死對象。如果套接字無法連接,你希望它做什麼? – 2013-04-27 10:51:19

2

當你調用start()上線,經過一段時間的推移線程實際運行之前。你打電話send()run()之前被調用,因此前out被初始化。不要這樣做。等待來自正在運行的線程的消息,然後才能安全地呼叫send()。您可以使用簡單的Object上的wait()notify()來執行此操作。

只要避免客戶致電start() - 當然,請致電start()Network的構造函數。

+0

「等待來自正在運行的線程的消息」您能否更詳細地解釋一下這個問題?我將如何等待這條消息? – skiwi 2013-04-26 20:38:15