2017-01-06 5 views
0

私はこの興味深いチュートリアルを複数のクライアント - サーバーチャットモデルで見つけました。私は一つの事を除いてすべてを理解しました。クラスMultiThreadChatServerSyncの実装では、彼は私達が私の同期方法についての記事をたくさん読んだJavaマルチスレッド。スレッドクラスの同期(this)

private final clientThread[] threads; 

フィールドにある同じリソースを共有しているため、異なるスレッドのコードの一部を同期する必要があることを説明し(これは)うまくいく。 'this'がスレッドのインスタンスを参照する場合、このコードはステートメントのブロックをどのように同期させますか?スレッド(0)がsynchronized(this)文を入力して、それ自身のモニタを取得するとしましょう。一方、thread [1]はsynchronized(this)文も入力しますが、スレッド[0]のモニタではなく、独自のインスタンスのモニタを取得するためブロックされません。誰かがこのコードが同期されている理由を私に説明することはできますか?何か不足していますか?

ここに記事へのリンクがあります。私は特定の部分にリンクすることができません。したがって、「public class MultiThreadChatServerSync」という文を検索してください。 http://makemobiapps.blogspot.com/p/multiple-client-server-chat-programming.html

ここには、ウェブページにアクセスできないユーザーのためのコードがあります。

import java.io.DataInputStream; 
import java.io.PrintStream; 
import java.io.IOException; 
import java.net.Socket; 
import java.net.ServerSocket; 

/* 
* A chat server that delivers public and private messages. 
*/ 
public class MultiThreadChatServerSync { 

    // The server socket. 
    private static ServerSocket serverSocket = null; 
    // The client socket. 
    private static Socket clientSocket = null; 

    // This chat server can accept up to maxClientsCount clients' connections. 
    private static final int maxClientsCount = 10; 
    private static final clientThread[] threads = new  clientThread[maxClientsCount]; 

    public static void main(String args[]) { 
    // The default port number. 
    int portNumber = 2222; 
    if (args.length < 1) { 
     System.out.println("Usage: java MultiThreadChatServerSync <portNumber>\n" + "Now using port number=" + portNumber); 
    } else { 
     portNumber = Integer.valueOf(args[0]).intValue(); 
    } 

    /* 
    * Open a server socket on the portNumber (default 2222). Note that we can 
    * not choose a port less than 1023 if we are not privileged users (root). 
    */ 
    try { 
     serverSocket = new ServerSocket(portNumber); 
    } catch (IOException e) { 
     System.out.println(e); 
    } 

    /* 
    * Create a client socket for each connection and pass it to a new client 
    * thread. 
    */ 
    while (true) { 
     try { 
     clientSocket = serverSocket.accept(); 
     int i = 0; 
     for (i = 0; i < maxClientsCount; i++) { 
      if (threads[i] == null) { 
      (threads[i] = new clientThread(clientSocket, threads)).start(); 
      break; 
      } 
     } 
     if (i == maxClientsCount) { 
      PrintStream os = new PrintStream(clientSocket.getOutputStream()); 
      os.println("Server too busy. Try later."); 
      os.close(); 
      clientSocket.close(); 
     } 
     } catch (IOException e) { 
     System.out.println(e); 
     } 
    } 
    } 
} 
/* 
* The chat client thread. This client thread opens the input and the output 
* streams for a particular client, ask the client's name, informs all the 
* clients connected to the server about the fact that a new client has joined 
* the chat room, and as long as it receive data, echos that data back to all 
* other clients. The thread broadcast the incoming messages to all clients and 
* routes the private message to the particular client. When a client leaves the 
* chat room this thread informs also all the clients about that and terminates. 
*/ 
class clientThread extends Thread { 

    private String clientName = null; 
    private DataInputStream is = null; 
    private PrintStream os = null; 
    private Socket clientSocket = null; 
    private final clientThread[] threads; 
    private int maxClientsCount; 

    public clientThread(Socket clientSocket, clientThread[] threads) { 
    this.clientSocket = clientSocket; 
    this.threads = threads; 
    maxClientsCount = threads.length; 
    } 

    public void run() { 
    int maxClientsCount = this.maxClientsCount; 
    clientThread[] threads = this.threads; 

    try { 
     /* 
     * Create input and output streams for this client. 
     */ 
     is = new DataInputStream(clientSocket.getInputStream()); 
     os = new PrintStream(clientSocket.getOutputStream()); 
     String name; 
     while (true) { 
     os.println("Enter your name."); 
     name = is.readLine().trim(); 
     if (name.indexOf('@') == -1) { 
      break; 
     } else { 
      os.println("The name should not contain '@' character."); 
     } 
     } 

     /* Welcome the new the client. */ 
     os.println("Welcome " + name 
     + " to our chat room.\nTo leave enter /quit in a new line."); 
     synchronized (this) { 
     for (int i = 0; i < maxClientsCount; i++) { 
      if (threads[i] != null && threads[i] == this) { 
      clientName = "@" + name; 
      break; 
      } 
     } 
     for (int i = 0; i < maxClientsCount; i++) { 
      if (threads[i] != null && threads[i] != this) { 
      threads[i].os.println("*** A new user " + name + " entered the chat room !!! ***"); 
      } 
     } 
     } 
     /* Start the conversation. */ 
     while (true) { 
     String line = is.readLine(); 
     if (line.startsWith("/quit")) { 
      break; 
     } 
     /* If the message is private sent it to the given client. */ 
     if (line.startsWith("@")) { 
      String[] words = line.split("\\s", 2); 
      if (words.length > 1 && words[1] != null) { 
      words[1] = words[1].trim(); 
      if (!words[1].isEmpty()) { 
       synchronized (this) { 
       for (int i = 0; i < maxClientsCount; i++) { 
        if (threads[i] != null && threads[i] != this 
         && threads[i].clientName != null 
         && threads[i].clientName.equals(words[0])) { 
        threads[i].os.println("<" + name + "> " + words[1]); 
        /* 
        * Echo this message to let the client know the private 
        * message was sent. 
        */ 
        this.os.println(">" + name + "> " + words[1]); 
        break; 
        } 
       } 
       } 
      } 
      } 
     } else { 
      /* The message is public, broadcast it to all other clients. */ 
      synchronized (this) { 
      for (int i = 0; i < maxClientsCount; i++) { 
       if (threads[i] != null && threads[i].clientName != null) { 
       threads[i].os.println("<" + name + "> " + line); 
       } 
      } 
      } 
     } 
     } 
     synchronized (this) { 
     for (int i = 0; i < maxClientsCount; i++) { 
      if (threads[i] != null && threads[i] != this && threads[i].clientName != null) { 
      threads[i].os.println("*** The user " + name + " is leaving the chat room !!! ***"); 
      } 
     } 
     } 
     os.println("*** Bye " + name + " ***"); 

     /* 
     * Clean up. Set the current thread variable to null so that a new client 
     * could be accepted by the server. 
     */ 
     synchronized (this) { 
     for (int i = 0; i < maxClientsCount; i++) { 
      if (threads[i] == this) { 
      threads[i] = null; 
      } 
     } 
     } 
     /* 
     * Close the output stream, close the input stream, close the socket. 
     */ 
     is.close(); 
     os.close(); 
     clientSocket.close(); 
    } catch (IOException e) { 
    } 
    } 
} 
+0

これはこれとは異なります。これを使うのではなく、 'a'と' b'を呼び出すとどうなりますか? – matt

+0

正確に私のポイント。彼らはこれとは異なります。つまり、コードは実際には同期されません。私は正しい? – Patrick

+0

このコードは実際にそこに畳み込まれています。それはrunメソッドclientThread [] threads = this.threads;の中に自身のようなスレッドの配列を作成します。それはいい見えません。 –

答えて

0

著者は、「すべてのsynchronized(this){}文が相互除外する。それは意味し、...」と述べている

あなたが文脈からそれを取る場合は、その文が露骨に偽です。

特定の例では、作成者がsynchronized(this) {...}ステートメントのすべてについて話していて、すべてが同じクラスに属するインスタンスメソッドにあり、クラスのインスタンスが1つしかない場合いつでも使用されています。

+0

synchronized(ClassNameOfObject){}を使用する方が良いと思いますか? – Patrick

+0

@Patrick、重要なことは、あなたのプログラムが保護する必要のある各_invariant_は、常に同じロックオブジェクトによって保護されているということです。不変量は、常に真でなければならない、プログラム内のデータ間の関係です。たとえば、キュ​​ー内のアイテムの数は、追加されたアイテムの数から削除された数を差し引いた数に等しい必要があります。同じキュークラスの2つの異なるインスタンスがある場合、それは2つの異なるインバリアントです。 1つのロックを使用して複数の不変量を保護することはOKですが、1つの不変量に​​複数のロックを使用することは決してできません。 –

+0

例のコードは醜いので、私はそれを勉強するのに多くの時間を費やしたくありません。数分から私はそれを見ることに費やしたが、著者が保護しようとしている不変量が何であるかは私にはっきりと分かっていない。 –