2017-01-06 3 views
0

Я наткнулся на это интересное руководство по нескольким клиент-серверным чат-моделям. Я все понял, кроме одного. В реализации класса MultiThreadChatServerSync, он объясняет, что нам нужно синхронизировать части коды из-за различные потоки, разделяющие один и тот же ресурс, который является полемJava многопоточность. synchronized (this) в классе Thread

private final clientThread[] threads; 

Я читал много статей о том, как синхронизировать (это работает. Как этот код синхронизирует блоки оператора, если «this» относится к экземпляру потока? Предположим, что поток [0] входит в синхронизированный (этот) оператор, поэтому он получает сам монитор. Тем временем thread [1] также вводит синхронизированный (этот) оператор, но он не будет заблокирован, потому что он будет получать монитор своего собственного экземпляра, а не монитор потока [0]. Может кто-нибудь объяснить мне, почему этот код синхронизирован? Я что-то упускаю?

Вот ссылка на статью. Я не могу ссылаться на конкретную часть. Поэтому просто найдите предложение «открытый класс 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

код действительно запутан там: он создает массив Threads, подобных самому себе в методе run, clientThread [] threads = this.threads ;. Это не выглядит красиво. –

ответ

0

автор заявляет, что «Все synchronized(this){} заявления исключают взаимно друг друга. Это означает, что ...»

Если вы берете его из контекста, это утверждение явно ложно.

Единственный контекст, который мог бы сделать его истинным, заключается в том, что автор говорит обо всех операциях synchronized(this) {...} в каком-либо конкретном примере, и все они находятся в экземплярах, принадлежащих к одному и тому же классу, и только один экземпляр класса когда-либо используется в любой момент времени.

+0

Как вы думаете, было бы лучше использовать синхронизированный (ClassNameOfObject) {}? – Patrick

+0

@Patrick. Важно то, что каждый _invariant_, который требуется защитить вашей программе, всегда охраняется одним и тем же объектом блокировки. Инвариантом являются некоторые отношения hp между данными в вашей программе, которые всегда должны быть истинными.Например, количество элементов в очереди должно всегда равняться количеству добавленных элементов минус число, которое было удалено. Если у вас есть два разных экземпляра одного и того же класса очереди, это два разных инварианта. Можно использовать один замок для защиты более одного инварианта, но никогда не имеет смысла использовать более одного замка для одного инварианта. –

+0

Код в примере уродлив, и я не хочу тратить много времени на его изучение. Через пару минут, которые я потратил на это, мне совершенно не ясно, что именно инварианты пытаются защитить автор. –

Смежные вопросы