Блокировка поля класса в многоп. среде
От: Foror http://foror.ru
Дата: 25.03.08 11:53
Оценка:
public class Channel {
  private List current;

  public synchronized String build() {
    // здесь нужна блокировка на current
    current.clear();
    current.add(some...);
    // здесь уже не нужна на current
  }

  public List getCurrent() {
    return current;
  }
}

public class ChannelMixer {

  private Channel channel;

  public synchronized String build() {
    List selected = new ArrayList();
    selected.addAll(channel.getCurrent()); // здесь нужно дождаться пока завершиться модификация current, и только затем его прочитать
  }
}


Кто-то может вызвать Channel#build(), в свою очередь параллельно может быть запущен ChannelMixer#build(). Мне нужно, чтобы во время Channel#build(), на месте где происходит модификация Channel#current выставлялась блокировка на это поле, чтобы никто другой из вне не мог читать это поле (т.е. ожидал пока завершится модификация current в build()).

Собственно как сделать блокировку на current?
Re: Блокировка поля класса в многоп. среде
От: Blazkowicz Россия  
Дата: 25.03.08 12:09
Оценка: +1
Здравствуйте, Foror, Вы писали:

F>Собственно как сделать блокировку на current?

synchronized(current)?
Re[2]: Блокировка поля класса в многоп. среде
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 25.03.08 13:45
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

F>>Собственно как сделать блокировку на current?

B>synchronized(current)?

Только нужно убедится, что a-la setCurrent() нигде нету.
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re: Блокировка поля класса в многоп. среде
От: ingie Россия  
Дата: 25.03.08 17:25
Оценка: 4 (1)
Здравствуйте, Foror, Вы писали:

F>Кто-то может вызвать Channel#build(), в свою очередь параллельно может быть запущен ChannelMixer#build(). Мне нужно, чтобы во время Channel#build(), на месте где происходит модификация Channel#current выставлялась блокировка на это поле, чтобы никто другой из вне не мог читать это поле (т.е. ожидал пока завершится модификация current в build()).


F>Собственно как сделать блокировку на current?


Блокировка это, конечно, хорошо, но её придётся делать почти везде где происходит вызов getCurrent().

Не силён в многопоточности, так что, пользуясь случаем хотелось бы задать вопрос знатокам...
Если на самом деле не нужно ожидать завершения модификации, можно ли написать так?

public class Channel {
  private List current;

  public String build() {
    List nc = new ArrayList();
    nc.add(some...);

    current = nc;
  }

  public List getCurrent() {
    return current;
  }
}
Re[2]: Блокировка поля класса в многоп. среде
От: Георгий  
Дата: 25.03.08 19:36
Оценка:
Здравствуйте, ingie, Вы писали:

I>Блокировка это, конечно, хорошо, но её придётся делать почти везде где происходит вызов getCurrent().


Речь шла о блокировке на current. В том числе, в методе getCurrent().

I>Если на самом деле не нужно ...


Вопрос в том, является ли присвоение атомарной операцией.
Re[3]: Блокировка поля класса в многоп. среде
От: Blazkowicz Россия  
Дата: 25.03.08 20:32
Оценка:
Здравствуйте, Георгий, Вы писали:

I>>Если на самом деле не нужно ...

Г>Вопрос в том, является ли присвоение атомарной операцией.

И на что эта атомарность влияет?
Re[3]: Блокировка поля класса в многоп. среде
От: ingie Россия  
Дата: 26.03.08 06:37
Оценка:
Здравствуйте, Георгий, Вы писали:

Г>Здравствуйте, ingie, Вы писали:


I>>Блокировка это, конечно, хорошо, но её придётся делать почти везде где происходит вызов getCurrent().


Г>Речь шла о блокировке на current. В том числе, в методе getCurrent().

Блокировка в getCurrent() не поможет, если где-то будет написано
c = getCurrent();
doSome(c);

Да и вообще она почти ничему не поможет.

Если уж с блокировками, то может лучше
class Channel {
...
  forEach(Visitor v) {
    synchronized(current) {
      for (o : current) {
        v.visit(o);
      }
    }
  }
...
}

И без getCurrent() вообще.
Иначе не будет гарантий как я понимаю. Или нет?

I>>Если на самом деле не нужно ...


Г>Вопрос в том, является ли присвоение атомарной операцией.

В current может лежать только старое значение или новое значение, никаких промежуточных вариантов быть не может,
как я понимаю. Допустим меня устраивают оба варианта.

Так можно или нет?
Re[4]: Блокировка поля класса в многоп. среде
От: Kuzz Россия  
Дата: 26.03.08 08:56
Оценка:
Здравствуйте, ingie, Вы писали:


I>Блокировка в getCurrent() не поможет, если где-то будет написано

I>
I>c = getCurrent();
I>doSome(c);
I>

I>Да и вообще она почти ничему не поможет.

Чтобы нельзя было сделать doSome(c), нужно, чтобы с было Immutable, неизменяемое. В данном случае, List можно возвращать
через Collections.unmodifiableList(list).


I>Если уж с блокировками, то может лучше

I>
I>class Channel {
I>...
I>  forEach(Visitor v) {
I>    synchronized(current) {
I>      for (o : current) {
I>        v.visit(o);
I>      }
I>    }
I>  }
I>...
I>}
I>

I>И без getCurrent() вообще.
I>Иначе не будет гарантий как я понимаю. Или нет?

А зачем такие сложнотсти? Загромоздите архитектуру..
Actions speak louder than words
Re[5]: Блокировка поля класса в многоп. среде
От: ingie Россия  
Дата: 26.03.08 10:48
Оценка:
Здравствуйте, Kuzz, Вы писали:


K>Чтобы нельзя было сделать doSome(c), нужно, чтобы с было Immutable, неизменяемое. В данном случае, List можно возвращать

K>через Collections.unmodifiableList(list).

Цель не будет достигнута. Так как:
    UnmodifiableCollection(Collection<? extends E> c) {
            if (c==null)
                throw new NullPointerException();
            this.c = c;
        }

    public Iterator<E> iterator() {
        return new Iterator<E>() {
        Iterator<? extends E> i = c.iterator();

        public boolean hasNext() {return i.hasNext();}
        public E next()      {return i.next();}
        public void remove() {
            throw new UnsupportedOperationException();
                }
        };
        }

Тогда уж надо
synchronized (current) {
a = new ArrayList();
a.addAll(current);
return a;
}



I>>Если уж с блокировками, то может лучше

I>>
I>>class Channel {
I>>...
I>>  forEach(Visitor v) {
I>>    synchronized(current) {
I>>      for (o : current) {
I>>        v.visit(o);
I>>      }
I>>    }
I>>  }
I>>...
I>>}
I>>

I>>И без getCurrent() вообще.
I>>Иначе не будет гарантий как я понимаю. Или нет?

K>А зачем такие сложнотсти? Загромоздите архитектуру..

Действительно, зачем? Я вообще-то сразу предложил обойтись без блокировок, только не знаю правильно — нет.
Re[6]: Блокировка поля класса в многоп. среде
От: Blazkowicz Россия  
Дата: 26.03.08 11:23
Оценка:
Здравствуйте, ingie, Вы писали:

I>Действительно, зачем? Я вообще-то сразу предложил обойтись без блокировок, только не знаю правильно — нет.

Правильно то что, если можно обойтись, то лучше обойтись.
Re: Блокировка поля класса в многоп. среде
От: RavshanKos Россия  
Дата: 26.03.08 11:37
Оценка: 5 (1)
Здравствуйте, Foror, Вы писали:

public class Channel {

  private Object mutexCurrent = new Object();
  private List current;

  public String build() {
    // здесь нужна блокировка на current
    synchronized(mutexCurrent) {
      current.clear();
      current.add(some...);
    }
    // здесь уже не нужна на current
  }

  public List getCurrent() {
    List ret = null;
    synchronized(mutexCurrent) {
      ret = new ArrayList(current);
    }

    return ret;
  }
}

public class ChannelMixer {

  private Channel channel;

  public synchronized String build() {
    List selected = new ArrayList();
    selected.addAll(channel.getCurrent()); // здесь нужно дождаться пока завершиться модификация current, и только затем его прочитать
  }
}

Поправьте меня, если я не прав. В этом случае Channel#getCurrent() вернёт список инстансов класса "some...", которые содержал Channel#current, на момент выполнения Channel#getCurrent(). Но само состояние инстансов "some..." может быть изменено в других потоках. Если хочешь 100% не изменённый снимок получить, используй клонирование внутри:
  public List getCurrent() {
    List ret = null;
    synchronized(mutexCurrent) {
      ret = clone(current);
    }

    return ret;
  }


F>Кто-то может вызвать Channel#build(), в свою очередь параллельно может быть запущен ChannelMixer#build(). Мне нужно, чтобы во время Channel#build(), на месте где происходит модификация Channel#current выставлялась блокировка на это поле, чтобы никто другой из вне не мог читать это поле (т.е. ожидал пока завершится модификация current в build()).
Re[4]: Блокировка поля класса в многоп. среде
От: Георгий  
Дата: 26.03.08 16:46
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

Г>>Вопрос в том, является ли присвоение атомарной операцией.


B>И на что эта атомарность влияет?


Ни на что не влияет. Опечатка в посте. Должно было быть так:

Вопрос в том, является ли присвоение атомарной операцией?

Re[5]: Блокировка поля класса в многоп. среде
От: Aib https://razborpoletov.com
Дата: 26.03.08 21:57
Оценка:
Здравствуйте, Георгий, Вы писали:

Г>Здравствуйте, Blazkowicz, Вы писали:


Г>>>Вопрос в том, является ли присвоение атомарной операцией.


B>>И на что эта атомарность влияет?


Г>Ни на что не влияет. Опечатка в посте. Должно было быть так:

Г>

Г>Вопрос в том, является ли присвоение атомарной операцией?


Простой ответ:
Строчка i = i;

1. i пихается в стек
2. параллельный тред изменяет i
3. в i пихается значение из стека

Я ответил на вопрос?
Re: Блокировка поля класса в многоп. среде
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 27.03.08 07:03
Оценка:
Здравствуйте, Foror, Вы писали:

[skipped]
F>Собственно как сделать блокировку на current?
Самое забавное, что по-вашему коду не видно, какая реализация интерфейса java.util.List используется. Собственно приведенный код вообще будет всегда материться посредством java.lang.NullPointerException. А это ключевой момент. Если, к примеру, в качестве реализации списка взять java.util.concurrent.CopyOnWriteArrayList, то блокировки изменения, возможно (!), вам будут и вовсе не нужны: читающий поток будет получать актуальное состояние коллекции на момент запроса — по описание не очень понял, устроит такое поведение или нет. В JDK 1.6 стоит посмотреть и другие реализации коллекций в пакете java.util.concurrent.

Другой простой, но вроде именно ваш вариант — использовать блокировку кода на основе java.util.concurrent.locks.ReentrantLock примерно так:
public class Channel<T> {
    private final ReentrantLock lockObj = new ReentrantLock();

    private final List<T> current = new ArrayList<T>();

    public String build() {
        lockObj.lock();
        try {
            // действия с current
        } finally {
            lockObj.unlock();
        }
    }

    public List<T> getCurrent() {
        final List<T> result = null;
        lockObj.lock();
        try {
            result = current;
        } finally {
            lockObj.unlock();
        }
        return result;
    }
}
Суть та же, что и стандартная блокировка (synchronized) по изменяемому объекту (current), но производительность выше. И потом, самое главное — это избавить клиентский код от необходимости блокировки: потокобезопасные классы должны по умолчанию предоставлять безопасный код, то есть не требующий дополнительных проверок на вызывающей стороне.
Re[2]: Блокировка поля класса в многоп. среде
От: dshe  
Дата: 27.03.08 11:38
Оценка:
Здравствуйте, rsn81, Вы писали:

R>Другой простой, но вроде именно ваш вариант — использовать блокировку кода на основе java.util.concurrent.locks.ReentrantLock примерно так:

R>public class Channel<T> {
R>...
R>    public List<T> getCurrent() {
R>        final List<T> result = null;
R>        lockObj.lock();
R>        try {
R>            result = current;
R>        } finally {
R>            lockObj.unlock();
R>        }
R>        return result;
R>    }
R>}


В данном примере, синхронизация в getCurrent не гарантирует, что возвращенный List<T> не будет изменен в другом потоке при последующем вызове build. Как правильно заметил RavshanKos
Автор: RavshanKos
Дата: 26.03.08
, имеет смысл подумать о том, чтобы вернуть копию current, а не непосредственно current.

R>Суть та же, что и стандартная блокировка (synchronized) по изменяемому объекту (current), но производительность выше.


Откуда известно, что производительность java.util.concurrent.locks.ReentrantLock выше, чем у стандатного synchronized?
--
Дмитро
Re[2]: Блокировка поля класса в многоп. среде
От: Foror http://foror.ru
Дата: 27.03.08 11:56
Оценка: +1
Здравствуйте, rsn81, Вы писали:

R>читающий поток будет получать актуальное состояние коллекции на момент запроса — по описание не очень понял, устроит такое поведение или нет.

Нет. Пока выполняется добавления/удаления в current, другие должны ждать завершения всех этих операций.

R>Другой простой, но вроде именно ваш вариант — использовать блокировку кода на основе java.util.concurrent.locks.ReentrantLock примерно. Суть та же, что и стандартная блокировка (synchronized) по изменяемому объекту (current), но производительность выше.

Почему это быстрее, и даже если это быстрее почему бы компилятору не сделать оптимизацию под этот вариант? У меня сейчас так: synchronized(current) {current.add(some...);}

R>} И потом, самое главное — это избавить клиентский код от необходимости блокировки: потокобезопасные классы должны по умолчанию предоставлять безопасный код, то есть не требующий дополнительных проверок на вызывающей стороне.


Вот это не не понял.
Re[3]: Блокировка поля класса в многоп. среде
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 27.03.08 12:47
Оценка:
Здравствуйте, dshe, Вы писали:

D>Откуда известно, что производительность java.util.concurrent.locks.ReentrantLock выше, чем у стандатного synchronized?

От Гетца.
Re[3]: Блокировка поля класса в многоп. среде
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 27.03.08 12:53
Оценка:
Здравствуйте, dshe, Вы писали:

D>В данном примере, синхронизация в getCurrent не гарантирует, что возвращенный List<T> не будет изменен в другом потоке при последующем вызове build. Как правильно заметил RavshanKos
Автор: RavshanKos
Дата: 26.03.08
, имеет смысл подумать о том, чтобы вернуть копию current, а не непосредственно current.

Угу, не учел. Как другой вариант, можно, если это катит по спецификации на клас, вернуть неизменяемую коллекцию с помощью Collections.unmodifiableList.

Кстати, по поводу java.util.concurrent.locks.ReentrantLock: коллекция java.util.concurrent.CopyOnWriteArrayList для перераспределения (копирование части массива) коллекции во время изменения, насколько помню, использует именно эту блокировку.
Re[4]: Блокировка поля класса в многоп. среде
От: dshe  
Дата: 27.03.08 13:52
Оценка: 16 (1)
Здравствуйте, rsn81, Вы писали:

R>Здравствуйте, dshe, Вы писали:


D>>Откуда известно, что производительность java.util.concurrent.locks.ReentrantLock выше, чем у стандатного synchronized?

R>От Гетца.

http://safari.oreilly.com/0321349601/ch13lev1sec4

13.4. Choosing Between Synchronized and ReentrantLock
(Java Concurrency in Practice)

ReentrantLock provides the same locking and memory semantics as intrinsic locking, as well as additional features such as timed lock waits, interruptible lock waits, fairness, and the ability to implement non-block-structured locking. The performance of ReentrantLock appears to dominate that of intrinsic locking, winning slightly on Java 6 and dramatically on Java 5.0.
...

Во как! Но читаем далее

...
ReentrantLock is an advanced tool for situations where intrinsic locking is not practical. Use it if you need its advanced features: timed, polled, or interruptible lock acquisition, fair queueing, or non-block-structured locking. Otherwise, prefer synchronized.
...

И наконец (последний абзац следовало бы выделить целиком)

...
Future performance improvements are likely to favor synchronized over ReentrantLock. Because synchronized is built into the JVM, it can perform optimizations such as lock elision for thread-confined lock objects and lock coarsening to eliminate synchronization with intrinsic locks (see Section 11.3.2); doing this with library-based locks seems far less likely. Unless you are deploying on Java 5.0 for the foreseeable future and you have a demonstrated need for ReentrantLock's scalability benefits on that platform, it is not a good idea to choose ReentrantLock over synchronized for performance reasons.

--
Дмитро
Re[4]: Блокировка поля класса в многоп. среде
От: dshe  
Дата: 27.03.08 13:59
Оценка:
Здравствуйте, rsn81, Вы писали:

R>Здравствуйте, dshe, Вы писали:


D>>В данном примере, синхронизация в getCurrent не гарантирует, что возвращенный List<T> не будет изменен в другом потоке при последующем вызове build. Как правильно заметил RavshanKos
Автор: RavshanKos
Дата: 26.03.08
, имеет смысл подумать о том, чтобы вернуть копию current, а не непосредственно current.

R>Угу, не учел. Как другой вариант, можно, если это катит по спецификации на клас, вернуть неизменяемую коллекцию с помощью Collections.unmodifiableList.

Не думаю, что это поможет. Она ведь должна быть неизменяема внутри, а не снаружи.
--
Дмитро
Re[5]: Блокировка поля класса в многоп. среде
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 27.03.08 15:20
Оценка: 6 (1)
Здравствуйте, dshe, Вы писали:

D>http://safari.oreilly.com/0321349601/ch13lev1sec4

D>13.4. Choosing Between Synchronized and ReentrantLock
D>(Java Concurrency in Practice)
[skipped]
Да, читал все это больше года назад (может ошибаюсь, но вроде бы тогда Гетц еще не опубликовал эту книгу; кстати, когда ее переведут?), только в переводе на ibm.com: Теория и практика Java: Более гибкая, масштабируемая блокировка в JDK 5.0. Новые классы блокировки усовершенствуют synchronized &mdash; но не спешите списывать synchronized со счетов. Кроме дополнительных возможностей, ReentrantLock выигрывает у synchronized во всех остальных отношениях (об этом Гетц четко пишет), к примеру, имеет лучшую пропускную способность в условиях активных гонок.



Из минусов ReentrantLock, пожалуй, можно привести только то, что он требует большей ответственности при программировании: не забывать unlock в блоке finally — с synchronized это делает за вас виртуальная машина. Ну а основной аргумент, почему не стоит отказываться от synchronized — это преемственность (про synchronized знает любой относительно не новичок в Java, новыми возможностями платформы интересуются в основном только профессионалы), большая функциональная поддержка со стороны виртуальной машины, ну и ожидания, что в новых версиях JVM synchronized будет улучшен. Гетц четко и говорит, цитирую:

Классы блокировки в java.util.compatible.lock — это передовой инструментарий для продвинутых пользователей и сложных ситуаций.

Ну и самое главное, претензию вашу не очень понял: выше я вовсе не утверждал, что нужно железно заменить synchronized на ReentrantLock — а только привел альтернативный вариант кода, ну и упомянул, что он в общем случае более производительный. Вроде бы нигде не соврал.
Re[5]: Блокировка поля класса в многоп. среде
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 27.03.08 15:34
Оценка:
Здравствуйте, dshe, Вы писали:

D>Не думаю, что это поможет. Она ведь должна быть неизменяема внутри, а не снаружи.

Быть может, видимо, не до конца понял, чего же нужно автору.
Re[3]: Блокировка поля класса в многоп. среде
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 27.03.08 15:34
Оценка:
Здравствуйте, Foror, Вы писали:

F>Вот это не не понял.

Теория и практика Java: Характеристика безопасности потока. Безопасность потока не является понятием типа "все-или-ничего":

Безопасность потока

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

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

Re[6]: Блокировка поля класса в многоп. среде
От: dshe  
Дата: 28.03.08 07:34
Оценка: +1
Здравствуйте, rsn81, Вы писали:

R>...Ну и самое главное, претензию вашу не очень понял: выше я вовсе не утверждал, что нужно железно заменить synchronized на ReentrantLock — а только привел альтернативный вариант кода, ну и упомянул, что он в общем случае более производительный. Вроде бы нигде не соврал.


Мне показалось спорным утверждение о том, что ReentrantLock более производительней, чем старый добрый synchronized. Это заставило меня порыться в Интернете и я кое-что для себя выяснил. За это вам отдельное спасибо. Признаюсь, я был удивлен обнаружив подтверждения тому, что противоречило моим представлениям. Я пока не вижу принципиальных причин почему synchronized должен быть медленнее ReentrantLock (ведь в крайнем случае JVM могла бы неявно использовать RL в качестве реализации synchronized). И то, что synchronized в Java 5 работает медленнее мне кажется не более чем досадным недоразумением. Еще раз спасибо за ссылки.
--
Дмитро
Re[7]: Блокировка поля класса в многоп. среде
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 28.03.08 07:53
Оценка:
Здравствуйте, dshe, Вы писали:

D>Мне показалось спорным утверждение о том, что ReentrantLock более производительней, чем старый добрый synchronized.

Угу, само появление ReentrantLock и та статья Гетца, несмотря на все увещевания и предостережения, вообще полны противоречий: казалось бы, изобрели более качественный альтернативный инструмент, а тем не менее... Мне кажется, ReentrantLock только временно выигрывает у synchronized, дорихтуют. То есть, разумеется, проектировался просто как инструмент с более широким функционалом, нежели synchronized, но раз уж получился впридачу и более быстрым (временно), ну что ж тут поделать... не замедлять же!

D>Это заставило меня порыться в Интернете и я кое-что для себя выяснил. За это вам отдельное спасибо. Признаюсь, я был удивлен обнаружив подтверждения тому, что противоречило моим представлениям. Я пока не вижу принципиальных причин почему synchronized должен быть медленнее ReentrantLock (ведь в крайнем случае JVM могла бы неявно использовать RL в качестве реализации synchronized).

Да вроде не могла бы: ReentrantLock — обычный исполняемый байт-код, а synchronized реализован на уровне виртуальной машины (зашит в язык), за счет чего на основе synchronized виртуальная машина потенциально может осуществлять некоторые манипуляции с программой (что-то Гетц там про это писал, точно не помню).

D>И то, что synchronized в Java 5 работает медленнее мне кажется не более чем досадным недоразумением. Еще раз спасибо за ссылки.

Угу, согласен. Один нюанс только: вендоров и виртуальных машин много, а JDK — один.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.