【问题标题】:Multithreaded server shared ArrayList多线程服务器共享 ArrayList
【发布时间】:2013-11-22 19:35:54
【问题描述】:

我正在尝试在 Java 中实现多线程服务器,其中服务器为连接到它的每个客户端生成一个新线程。通信在实现 Runnable 接口并以 Socket 描述符作为输入的类中进行。当一个新客户端连接时,我会使用 Socket 描述符编号生成一个新线程。

我需要在服务器上维护两个 ArrayList,只要有新客户端加入系统(客户端发送的一些数据)就会更新它们。如何实现在不同线程中运行的多个客户端之间共享 ArrayList 的行为?

【问题讨论】:

  • 一个静态向量可能是您正在寻找的。​​span>
  • @JustinKSU 当然,如果你想在你的脚下开枪:)
  • java.util.concurrent 的东西会更好。

标签: java multithreading sockets concurrency arraylist


【解决方案1】:

您可以在主服务器线程中创建并发集合的一个实例,然后通过构造函数将其传递给每个Runnable 套接字处理程序。 (听起来你已经在做这样的事情来传递Socket 本身。)

CopyOnWriteArrayList 是一个并发的List 实现,但它并不是特别有效。还有其他支持并发访问的集合类型,可能会提供更好的性能。

【讨论】:

    【解决方案2】:

    如果您预计会有很多客户端并希望随机访问它们,请使用ConcurrentSkipListMap。这为您提供了一个高效、线程安全、非阻塞的 Map。使用索引号作为键,或者使用更有意义的东西。如果您只需要一个简单的列表(无随机访问),请使用 ConcurrentLinkedQueue,除了它是单链表而不是 Map 之外,这几乎是一样的。

    另一种方法是使用 ArrayList(或数组,就此而言),但将其视为不可变的。当您需要添加客户端时,复制旧客户端,将客户端添加到该客户端,然后将新客户端替换为旧客户端。 (CopyOnWriteArrayList,正如 erickson 建议的那样,为您执行此操作。)这将在维护列表时花费您,但可以让大量线程同时获取并使用该列表。诚然,只有当您需要在每次添加后处理列表、可能对其进行排序以及添加和删除一些受添加影响的条目时,这种技术才会真正有用。)

    【讨论】:

      【解决方案3】:

      如果您希望 ArrayLists 存储每个客户端的信息并通过客户端 ID 访问它,我建议只使用带有客户端 ID 作为键的 java.util.concurrent.ConcurrentHashMap,因为这非常容易实现。

      如果您真的需要ArrayList,我会将其作为实现细节隐藏在服务器代码中。然后你会从你的Runnables 调用一些服务器方法来更新ArrayList。通过这种方式,您可以从非常简单的事情开始,例如:

      public class Server {
          ...      
          private ArrayList<Info> infos = new ArrayList<Info>();
          ...
          // Create one method for each *abstract* operation
          // that you need to perform on the ArrayList
          // (Do *NOT* implement read / write primitives
          // unless that is exactly what you need. The idea is
          // that all synchronization that you might need on 
          // the ArrayList happens inside those methods, so
          // the Runnables are unaware of the implementation
          // you haven chosen.)
      
          // Here I have used example operations that are
          // a bit more complex than read / write primitives
          // so the intention is more clear.
      
          public void clearInfo(int pos) {
              synchronized (infos) {
                  infos.set(pos, null);
              }
          }
      
          public Info updateInfo(int pos, Info info) {
              Info oldInfo = null;
              synchronized (infos) {
                  oldInfo = infos.get(pos);
                  infos.set(pos, info);
              }
              return oldInfo;
          }
          ...
          public Info readInfo(int pos) {
              Info info = null;
              synchronized (infos) {
                  infos.get(pos);
                  info.incrementReadCount();
              }
              return null;
          }
          ...
      }
      ...
      public class ClientRunnable implements Runnable {
          ...
          private Server server;
          ...
          @Override
          public void run() {
              ...
              Info info = // getInfo();
              int writePos = // getWritePos();
              Info old = server.updateInfo(writePos, info);
              ...
              int readPos = // getReadPos();            
              Info newInfo = server.readInfo(readPos);
              ...
          }
          ...
      }
      

      然后,当您分析您的应用程序时,如果您发现对列表的访问存在争用,您可以微调锁定,甚至将您的实现更改为无锁的,同时最大限度地减少对代码的影响。如今,Java 中的锁定速度非常快,因此在许多情况下,这种简单的解决方案可能就足够了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-25
        相关资源
        最近更新 更多