多线程服务器共享ArrayList

时间:2013-11-22 19:35:55

标签: java multithreading sockets concurrency arraylist

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

我需要在服务器上维护两个ArrayLists,每当新客户端加入系统时(使用客户端发送的一些数据)都会更新。如何在不同线程中运行的多个客户端之间实现共享ArrayList的这种行为?

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

如果您期望很多客户并希望随机访问它们,请使用ConcurrentSkipListMap。这为您提供了一个高效,线程安全,无阻塞的Map。使用索引号作为键,或使用更有意义的东西。如果你只需要一个简单的列表(没有随机访问),可以使用ConcurrentLinkedQueue,除了它是一个单链表而不是Map之外,这是完全相同的。

另一种方法是使用ArrayList(或数组),但将其视为不可变的。当您需要添加客户端时,复制旧客户端,将客户端添加到客户端,然后将旧客户端替换为旧客户端。 (CopyOnWriteArrayList,正如erickson建议的那样,为你做这件事。)这会在维护列表时花费你,但让成群的线程得到控制并一次性使用列表。不可否认,当您需要在每次添加后处理列表,可能对其进行排序,以及添加和删除受添加影响的一些条目时,此技术才变得非常有用。)

答案 2 :(得分:0)

如果您希望ArrayList存储每个客户端信息并通过客户端ID访问它,我建议使用java.util.concurrent.ConcurrentHashMap并将客户端ID作为密钥,因为这非常简单易于实现

如果您确实需要ArrayList,我会将其隐藏为服务器代码中的实现细节。然后,您可以从Runnable调用一些服务器方法来更新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的锁定速度非常快,所以在很多情况下这个简单的解决方案可能已经足够好了。