2014-10-23 3 views
1

Вот моя проблема. Я хочу документировать часть программы и хочу что-то выразить, я считаю довольно распространенным среди сообщества программистов, но не могу взломать идею в одно слово. Взгляните на этот класс:Невозможно найти идиоматику дизайна/программирования за интуитивно-хорошей практикой: дайте объекту построить детерминированный период

public class RemoteClient { 
    private InetSocketAddress remoteAddress; 
    /*+ some other fields not in the scope of this topic*/ 

    public RemoteClient(String hostName, int port 
    /*+ some other arguments not in the scope of this topic*/){ 
     this.remoteAddress=new InetSocketAddress(hostName, port); 
    } 

    public InetSocketAddress getRemoteAddress() { 
     return this.remoteAddress; 
    } 

} 

Очевидной проблемой в многопоточной среде является то, что вызывающий конструктор не имеет никаких гарантий, что hostName будет решена, и, следовательно, время сборки не является детерминированным. Было бы, как мне кажется, гораздо лучше, если строительство InetSocketAddress поленился, такие как следующие:

public class RemoteClient { 
    private volatile InetSocketAddress remoteAddress; 
    private final String hostName; 
    private final int port; 
    /*+ some other fields not in the scope of this topic*/ 

    public RemoteClient(String hostName, int port 
    /*+ some other arguments not in the scope of this topic*/){ 
     this.hostName=hostName; 
     this.port=port; 
    } 

    /** lazy instanciation **/ 
    public InetSocketAddress getRemoteAddress() { 
     if(remoteAddress==null){ 
      synchronized(hostName){ 
       if(remoteAddress==null){ 
        this.remoteAddress=new InetSocketAddress(hostName, port); 
       } 
      } 
     } 
     return this.remoteAddress; 
    } 

} 

Таким образом, в этом случае Thread вызова оператора конструктора, который мог бы на какое-то время строить из списка имена хостов, больше не связано с сетью. Теперь он делегирован Thread, который отвечает за связь с удаленным клиентом.

    Мои вопросы:
  • вы можете указать причины, почему второе утверждение/не лучше разработаны?
  • не могли бы вы сказать слово на идиоме за этой практикой?

Я считаю, что это не мнение на основе темы, как я прошу дизайн.

+0

'двойная проверка блокировки'? – EpicPandaForce

+0

Ленивая инициализация кажется более подходящей. Двойная проверка блокировки - это то, что вам нужно сделать для ее достижения. – pimaster

+0

Помимо того факта, что вы на самом деле используете двойной шаблон блокировки, я бы сказал, что шаблон, который вы ищете, уже находится в вашем комментарии: ** ленивый instanciation **. Термин * блаженный * ** Lazy Loading **. – Oncaphillis

ответ

2

Простой, «сделать это в конструкторе метода времени», что вы не любите есть несколько больших преимуществ:

  1. remoteAddress может быть final и более потокобезопасной, e.g. leaky constructors. И для простоты он избегает всего двойного замка. (Кстати, если вы не вызываете getRemoteAddress() Gazillion раз, просто синхронизировать ваш ленивый вызов.)
  2. Если абонент дает неверный порт, он не сможет быстро (бросить IllegalArgumentException) в конструкторе который, вероятно, лучше место для обработки ошибки. Ленивая версия getRemoteAddress() не удастся значительно позже с IllegalArgumentException в методе, который не имеет аргументов, что очень сбивает с толку. Плюс к тому времени, вероятно, слишком поздно предпринять какие-либо корректирующие действия (например, вернуться к уровню пользовательского интерфейса, чтобы снова спросить в диалоговом окне).

Для записи я согласен с EJP в том, что конструктор без детерминированных скоростей вряд ли будет проблемой в любом случае.

+0

100% согласен. Я считаю, что лучшим решением здесь является, как @EJB, «InetSocketAddress.createUnresolved (String host, int port)» во время построения; дает мне преимущества обоих решений. –

+1

Престижность к EJP для 'createUnresolved()' - Я никогда не знал об этом до сегодняшнего дня. – user949300

0

В этом случае вам не нужно использовать synchronized с volatile. Неустойчивость делает всю работу за вас. C.f. the Java Language Spec:

Запись в летучую переменную V (§8.3.1.4) синхронизирует-со всеми последующим чтением V любым потоком (где «последующим» определяются в соответствии с порядком синхронизацией).

И это StackOverflow ответ на Safe Publication.

public class RemoteClient { 
    private volatile InetSocketAddress remoteAddress; 
    private final String hostName; 
    private final int port; 
    /*+ some other fields not in the scope of this topic*/ 

    public RemoteClient(String hostName, int port 
    /*+ some other arguments not in the scope of this topic*/){ 
     this.hostName=hostName; 
     this.port=port; 
    } 

    /** lazy instanciation **/ 
    public InetSocketAddress getRemoteAddress() { 
     if(remoteAddress==null){ 
      this.remoteAddress=new InetSocketAddress(hostName, port); 
     } 
     return this.remoteAddress; 
    } 

} 
+0

Это звучит неправильно для меня. «Запись в изменчивую переменную синхронизируется со всеми последующими чтениями, что означает синхронизацию * при записи *. В вашем фрагменте два потока могли бы, следовательно, достигнуть строки 'if (remoteAddress == null)' и, следовательно, создать каждый из них один экземпляр, хотя один из них сделает это первым из-за этой цитаты, которую вы дали о 'volatile'. Двойная проверка - это не вариант. 'volatile' ** позволяет синхронизировать записи, а не чтения. ** –

+0

Ну, здесь всевозможные странные вещи. Да, могут быть созданы два объекта. Однако все остальное, что вы сказали, неверно. Спектр означает, что он говорит, даже если это «кажется неправильным». Синхронизация в Java почти полностью связана с расами данных: запись, за которой следует последующее чтение. Слишком много, чтобы войти сюда. Если вы действительно сомневаетесь, задайте другой вопрос о «volatile» и синхронизации с последующими чтениями, чтобы получить более полный ответ. – markspace

+0

Извините, мое последнее предложение было не очень ясным. Английский не мой родной язык. Я имел в виду, что синхронизация начинается, когда в поле записывается ссылка. Так сказать, мне нужен блок синхронизации, который бы вложил мою операцию чтения '== null' и операцию записи. В противном случае, даже с изменчивым ключом, у меня может быть N экземпляров объекта, висящего вокруг. Наличие N экземпляров не является такой проблемой? –

2

Очевидной проблемой в многопоточной среде является то, что вызывающий конструктор не имеет никаких гарантий, что будет решена Hostname, и, следовательно, время сборки не детерминированный.

Я этого не вижу. Я не понимаю, почему это «очевидная проблема в многопоточной среде», и я не понимаю, почему «недетерминированное время построения» является проблемой в какой-либо конкретной среде вообще. Если время строительства является проблемой, это проблема в любой среде. Если то, что вы ищете, это более короткий тайм-аут DNS, есть способы обеспечить его соблюдение: см. Страницу «Свойства сети» в Javadoc.

Это было бы, я считаю, намного лучше, если бы конструкция InetSocketAddress была ленивой.

В этом случае все, что вам нужно, это InetSocketAddress.createUnresolved().

+0

Случай использования: поток A получает информацию о хостах из своего рода источника. Каждый раз, когда он получает информацию о новом хосте, он создает экземпляр и сопоставляет его. Он не хочет, чтобы имя узла было разрешено, потому что на данном этапе хост не нужен. Он будет разрешен потоком B, который будет отвечать за связь с этим конкретным хостом. Кстати, я не использую 'InetSocketAddress' для построения' Socket'; а 'DatagramPacket'. Другим преимуществом любого «InetAddress» является то, что если домен не решен, вы сохраните след этого **, избегая бесполезного использования ресурсов. –

+0

NB: 'новый InetSocketAddress (hostName, порт);' компилирует home, jdk8u25. –

+0

В этом случае вам понадобится ['InetSocketAddress.createUnresolved()'] (http://docs.oracle.com/javase/7/docs/api/java/net/InetSocketAddress.html#createUnresolved (java.lang. String,% 20int)). – EJP