Блокировка поля класса в многоп. среде
От: 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.

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