2017-01-10 3 views
0

Предположим, у меня есть класс:Как гарантировать синхронизацию всех безобидных ссылок на объект?

public final class Server { 

private final ArrayList<ServerConnection> connections; 
private ServerConnection pending; 
private Thread connector; 

public Server() { 
    connections = new ArrayList<>(); 

    connector = new Thread(() -> { 
     while (true) { 
      pending = new ServerConnection(); 
      pending.waitForConnection(); 

      //Could be adding while another thread is iterating. 
      connections.add(pending); 
     } 
    }, "Connection Establisher"); 
    connector.setDaemon(true); 
    connector.setPriority(Thread.MIN_PRIORITY); 
    connector.start(); 
} 

//Anyone with a refrence to this object can access connections. 
public ArrayList<ServerConnection> getConnections() { 
    return connections; 
} 
} 

Как бы убедиться, что connections не используется в то время как я добавить объект. Я думал об использовании блока synchronized (connections) {...} в потоке, но, насколько мне известно, synchronized блокирует все безобидные ссылки на connections, должен быть в синхронизированном блоке. Есть ли способ, которым я могу убедиться, что весь небезопасный доступ к connections синхронизирован?

ответ

0

Выполнение синхронизированного метода getConnections недостаточно, потому что, как только вызывающий объект получает ссылку на список, он может делать с ним все, что захочет, включая небезопасные операции потока.

Несколько простых шагов сделает ваш код более надежным:

  • использовать только окончательные переменные
  • обеспечивают запуск и остановка метода. На данный момент вы запускаете поток в конструкторе и получаете ссылку на это - это может иметь странные эффекты видимости. И вы не справляетесь с остановкой потока, создавая поток вместо демона - он работает, но, вероятно, не так чист.

Простая переписка может выглядеть как код ниже (это, безусловно, может быть улучшено) - проверьте комментарии. Обратите внимание, что это будет работать лучше, если waitForConnection также отреагирует на прерывание, например, путем выброса InterruptedException.

public final class Server { 

    //us a thread safe list 
    private final List<ServerConnection> connections = new CopyOnWriteArrayList<>(); 
    //make the thread final 
    private final Thread connector; 

    public Server() { 
    connector = new Thread(() -> { 
     //provide a mechanism to stop the thread: exit on interruption 
     while (!Thread.currentThread().isInterrupted()) { 
     ServerConnection pending = new ServerConnection(); 
     pending.waitForConnection(); 

     //Could be adding while another thread is iterating. 
     connections.add(pending); 
     } 
    }, "Connection Established"); 
    //Note that the priority may be ignored at runtime 
    connector.setPriority(Thread.MIN_PRIORITY); 
    } 

    public void start() { 
    connector.start(); 
    } 

    //to stop the thread, interrupt it 
    public void stop() { 
    if (!connector.isAlive()) throw new IllegalStateException("The server is not started"); 
    connector.interrupt(); 
    } 

    //don't return the list but an unmodifiable view of the list 
    public List<ServerConnection> getConnections() { 
    return Collections.unmodifiableList(connections); 
    } 
} 

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

  • Нет связанных вопросов^_^