2013-11-22 3 views
1

Я пытаюсь реализовать многопотоковый сервер в Java, где сервер порождает новый поток для каждого клиента, который подключается к нему. Связь выполняется в классе, который реализует интерфейс Runnable и принимает дескриптор Socket как входной. Когда новый клиент подключается, я создаю новый поток с номером дескриптора Socket.Многопотоковый сервер shared ArrayList

Мне нужно сохранить на сервере два массива ArrayLists, которые обновляются каждый раз, когда новый клиент присоединяется к системе (с некоторыми данными, которые отправляет клиент). Как я могу выполнить такое поведение совместного использования ArrayList среди нескольких клиентов, работающих в разных потоках?

+0

Статический вектор может быть тем, что вы ищете. – JustinKSU

+1

@ JustinKSU конечно, если вы хотите стрелять в свои ноги :) –

+2

Что-то из java.util.concurrent было бы лучше. – JustinKSU

ответ

1

Вы можете создать один экземпляр параллельной коллекции в своем основном потоке сервера, а затем передать ее через конструктор для каждого из ваших обработчиков сокетов Runnable. (Похоже, что вы уже делаете что-то вроде этого, чтобы пройти сам Socket.)

CopyOnWriteArrayList является одновременно List реализации, но это не особенно эффективным. Существуют другие типы коллекций, которые поддерживают параллельный доступ и могут обеспечить лучшую производительность.

0

Если вы ожидаете большого количества клиентов и хотите получить к ним доступ случайным образом, используйте ConcurrentSkipListMap. Это дает вам высокоэффективную, потокобезопасную, неблокирующую карту. Используйте номера индексов в качестве ключей или используйте что-то более значимое. Если вам просто нужен простой список (без произвольного доступа), используйте ConcurrentLinkedQueue, что почти то же самое, кроме его односвязного списка вместо Карты.

Другим подходом было бы использовать ArrayList (или массив, если на то пошло), но относиться к нему как к неизменному. Когда вам нужно добавить клиента, скопируйте старый, добавьте к нему клиента, а затем замените новый на старый. (CopyOnWriteArrayList, как было предложено erickson, делает это за вас.) Это будет стоить вам при сохранении списка, но позволит вам размножаться потоками и использовать список сразу. По общему признанию, эта методика становится действительно полезной, когда вам нужно обрабатывать список после каждого добавления, возможно, сортировать его, а также добавлять и удалять несколько записей, на которые влияет добавление.)

0

Если вы хотите, чтобы ArrayList s сохранял per- информацию о клиенте и доступ к ней по идентификатору клиента, я предлагаю использовать только java.util.concurrent.ConcurrentHashMap с идентификатором клиента в качестве ключа, поскольку это очень просто реализовать.

Если вам действительно нужен ArrayList, я бы спрятал его как деталь реализации в код сервера. Затем вы вызовете некоторые методы сервера из своего Runnable s, чтобы обновить 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 довольно быстро, поэтому во многих случаях это простое решение может быть достаточно хорошим.

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