Конфайнмент - PullRequest
       35

Конфайнмент

36 голосов
/ 07 июня 2011

Я читаю Java Concurrency на практике и путаюсь с концепцией ограничения потока.В книге говорится, что

Когда объект ограничен потоком, такое использование автоматически поточно-ориентировано, даже если сам ограниченный объект не является

Так, когда объектограничен потоком, никакой другой поток не может иметь к нему доступ?Это то, что значит быть ограниченным потоком?Как сохранить объект в потоке?

Редактировать: Но что, если я все еще хочу поделиться объектом с другим потоком?Предположим, что после того, как поток A завершает работу с объектом O, поток B хочет получить доступ к O. В этом случае, может ли O по-прежнему ограничиваться B после того, как A покончил с этим?

Использование локальной переменной является одним из примеров дляконечно, но это просто означает, что вы не делитесь своим объектом с другим потоком (ВСЕ).В случае пула соединений JDBC, не передается ли одно соединение из одного потока в другой, когда поток завершает работу с этим соединением (совершенно не догадываясь об этом, потому что я никогда не использовал JDBC).

Ответы [ 8 ]

40 голосов
/ 07 июня 2011

Так, когда объект ограничен потоком, никакой другой поток не может иметь к нему доступ?

Нет, все наоборот: если вы гарантируете, что ни один другой поток не имеет доступа к объекту, то говорят, что этот объект ограничен одним потоком.

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

Например: если ссылка only на объект происходит из локальной переменной, тогда объект определенно ограничен одним потоком, поскольку другие потоки никогда не смогут получить доступ к локальным переменным .

Точно так же, если ссылка only на объект происходит от другого объекта, который, как уже было доказано, ограничен одним потоком, тогда этот первый объект ограничивается тем же потоком.

Редактирование объявления: На практике у вас может быть объект, к которому может обращаться только один поток за один раз в течение его жизни, но для которого этот единственный поток изменяется (объект JDBC Connection из соединения хороший пример).

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

И, на мой взгляд, эти объекты никогда не «ограничиваются одним потоком» (что подразумевает сильную гарантию), но можно сказать, что «они используются одним потоком одновременно».

10 голосов
/ 07 июня 2011

Наиболее очевидный пример - использование локального хранилища потоков.См. Пример ниже:

class SomeClass {
    // This map needs to be thread-safe
    private static final Map<Thread,UnsafeStuff> map = new ConcurrentHashMap<>();

    void calledByMultipleThreads(){
        UnsafeStuff mystuff = map.get(Thread.currentThread());
        if (mystuff == null){
            map.put(Thread.currentThread(),new UnsafeStuff());
            return;
        }else{
            mystuff.modifySomeStuff();
        }
    }
}

* * * * * * * * * * * * * * * * * * * * * * * * Объекты UnsafeStuff сами могут "использоваться совместно" с другими потоками в том смысле, что если вы передадите другой поток вместо Thread.currentThread() во время выполнения, на картуget метод, вы получите объекты, принадлежащие другим потокам.Но вы решили не .Это «использование, которое ограничено потоком».Другими словами, условия runtime таковы, что объекты в действительности никогда не разделяются между различными потоками.

С другой стороны, в приведенном ниже примере объект автоматически ограничивается потоком, и, так сказать, «сам объект» ограничивается потоком.Это в том смысле, что невозможно получить ссылку из других потоков независимо от условий выполнения:

class SomeClass {
    void calledByMultipleThreads(){
        UnsafeStuff mystuff = new UnsafeStuff();
        mystuff.modifySomeStuff();
        System.out.println(mystuff.toString());
    }
}

Здесь UnsafeStuff выделяется внутри метода и выходит из области видимости, когдаметод возвращает .. Другими словами, спецификация Java статически гарантирует, что объект всегда ограничен одним потоком.Таким образом, не условие выполнения или способ его использования обеспечивает ограничение, а скорее спецификацию Java.

Фактически, современная JVM иногда выделяет такие объекты в стеке, в отличие от первого примера (лично я не проверял это, но я не думаю, что, по крайней мере, современные JVM это делают).

Покадругими словами, в первом примере JVM не может быть уверена в том, что объект ограничен потоком, просто заглянув внутрь calledByMultipleThreads() (кто знает, что другие методы связывают с SomeClass.map).В последнем примере это может.

Редактировать: Но что, если я все еще хочу поделиться объектом с другим потоком?Предположим, что после того, как поток A завершает работу с объектом O, поток B хочет получить доступ к O. В этом случае, может ли O по-прежнему ограничиваться B после того, как с ним покончено?

Я не думаю, чтов этом случае оно называется «ограниченным».Когда вы делаете это, вы просто гарантируете, что к объекту нет доступа одновременно.Так работает параллелизм EJB.Вам все еще нужно «безопасно опубликовать» обсуждаемый общий объект в потоках.

6 голосов
/ 07 июня 2011

Так, когда объект ограничен потоком, никакой другой поток не может иметь к нему доступ?

Вот что означает ограничение потока - объект может быть доступен только НИКОГДА одному потоку.

Это то, что значит быть ограниченным потоком?

См. Выше.

Как удерживать объект в потоке?

Общий принцип заключается в том, чтобы не помещать ссылку куда-либо, что позволило бы ее видеть другому потоку. Немного сложно перечислить набор правил, которые это обеспечат, но (например), если

  • вы создаете новый объект, а
  • вы никогда не назначаете ссылку на объект для переменной экземпляра или класса, а
  • вы никогда не вызываете метод, который делает это для ссылки,
  • тогда объект будет ограничен потоком.
5 голосов
/ 07 июня 2011

Я думаю, это то, что хочу сказать.Например, создание объекта внутри метода run и отсутствие передачи ссылки на любой другой экземпляр.

Простой пример:

public String s;

public void run() {
  StringBuilder sb = new StringBuilder();
  sb.append("Hello ").append("world");
  s = sb.toString();
}

Экземпляр StringBuilder является поточно-ориентированным, поскольку он ограниченпоток (который выполняет этот метод запуска)

3 голосов
/ 07 июня 2011

Одним из способов является «ограничение стека», при котором объект представляет собой локальную переменную, ограниченную стеком потока, поэтому никакой другой поток не может получить к нему доступ.В приведенном ниже методе list является локальной переменной и не выходит из метода.Список не должен быть потокобезопасным, потому что он ограничен стеком выполняющегося потока.Никакой другой поток не может изменить его.

public String foo(Item i, Item j){
    List<Item> list = new ArrayList<Item>();
    list.add(i);
    list.add(j);
    return list.toString();
}

Другой способ ограничения объекта потоком - использование переменной ThreadLocal, которая позволяет каждому потоку иметь свою собственную копию.В приведенном ниже примере каждый поток будет иметь свой собственный объект DateFormat, поэтому вам не нужно беспокоиться о том, что DateFormat не является потокобезопасным, поскольку к нему не будут обращаться несколько потоков.

private static final ThreadLocal<DateFormat> df
                 = new ThreadLocal<DateFormat>(){
    @Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyyMMdd");
    }
  };

Дополнительная литература

0 голосов
/ 07 июня 2011

Я имею в виду, что только код, выполняющийся в одном потоке, получает доступ к объекту.

В этом случае объект не должен быть «потокобезопасным»

0 голосов
/ 07 июня 2011

Это именно то, что это значит. Доступ к самому объекту осуществляется только одним потоком, и поэтому он безопасен для потоков. ThreadLocal объекты - это объекты, которые связаны с единственным потоком

0 голосов
/ 07 июня 2011

См .: http://codeidol.com/java/java-concurrency/Sharing-Objects/Thread-Confinement/

Более формальное средство поддержания ограничение потока является ThreadLocal, который позволяет связать значение для потока с сохранением значения объект. Thread-Local обеспечивает получение и установить методы доступа, которые поддерживают отдельная копия значения для каждого поток, который его использует, поэтому get возвращает самое последнее значение, переданное для установки из текущего выполняющегося потока.

Он содержит копию объекта на один поток, поток A не может получить доступ к копии потока B и нарушает его инварианты, если вы сделаете это специально (например, присвойте значение ThreadLocal статической переменной или предоставьте его другими методами)

...