Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
queue.poll() на queue.take(), не игнорировать InterruptedException и убрать всю прочую синхронизацию на барьерах, CDL и т.д. class A {
public void asyncMethod(final Callback callback) {
newFixedThreadPool(1).execute(new Runnable() {
@Override
public void run() {
parkNanos(TimeUnit.SECONDS.toNanos(5));
callback.onFinish("Finished!");
}
});
}
public Object syncMethod() {
final Exchanger<Object> ex = new Exchanger<Object>();
asyncMethod(new Callback() {
@Override
public void onFinish(Object o) {
try {
ex.exchange(o);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
try {
return ex.exchange(null);
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
}
static Object syncMethod() {
final Object[] result = new Object[1];
final Thread caller = Thread.currentThread();
A.asyncMethod(new Callback() {
@Override
public void onFinish(Object obj) {
result[0] = obj;
LockSupport.unpark(caller);
}
});
LockSupport.park();
return result[0];
}
LockSupport.park() в любое время. («The park method may also return at any other time, for „no reason“»)while (result[0] == null){
LockSupport.park();
}
static Object syncMethod() {
final Object[] result = new Object[1];
final Thread caller = Thread.currentThread();
A.asyncMethod(new Callback() {
@Override
public void onFinish(Object obj) {
synchronized(result) {
result[0] = obj;
LockSupport.unpark(caller);
}
}
});
while (true){
synchronized(result) {
if (result[0] == null) LockSupport.park();
else break;
}
}
return result[0];
}
volatile и Object result вместо массива :) Но это будет работать только в том случае, если наш aSync никогда не должен вернуть null. BlockingQueue там, где мы используем CDL или CB? Да и в первом примере зачем List, если нам по-сути нужен просто один объект?Кстати зачем вообще BlockingQueue там, где мы используем CDL или CB?
Да и в первом примере зачем List, если нам по-сути нужен просто один объект?
И вообще, из всех предложенных вариантов, ИМХО, самый «элегантный» — с Exchanger.
А я не совсем :) Exchanger служит для двустороннего обмена, а в данном примере имеет место односторонний (producer-consumer). ИМХО лучше всего подходит SynchronousQueue. И по длине кода столько же: один exchange меняется на put, а второй — на take.И вообще, из всех предложенных вариантов, ИМХО, самый «элегантный» — с Exchanger.Согласен :)
The park method may also return at any other time, for «no reason»На практике этого никогда не произойдет, т.к. HotSpot фильтрует «случайные» пробуждения.
На практике этого никогда не произойдет, т.к. HotSpot фильтрует «случайные» пробуждения.
LockSupport понял, спасибо.заменив volatile store на обычный store. Программа будет абсолютно правильно работать на x86 архитектуре, хотя, согласно JMM, будет некорректной.
Callback не является реализацией интерфейсов ни Callable ни Runnable. И доступа к исходному коду библиотеки и темболее A.asyncMethod() нету.A.asyncMethod(new Callback() {
@Override
public void onFinish(Object o) {
synchronized (queue) {
queue.add(o);
queue.notify();
}
}
});
if (queue.size() == 0) {
synchronized (queue) {
if (queue.size() == 0) queue.wait();
}
}
return queue.poll();
— этот код некорректен. Пройдя по ветке queue.size != 0 вы вернете из списка объект, который может быть не до конца инициализирован. Ведь между сохранением объекта в список в другом потоке, и возвратом из списка в этом потоке у вас нет никаких ребер hb. Это типичный DCLObject o? Он инициализируется еще до вызова метода onFinish.synchronized (queue) { return queue.poll(); }
synchronized (queue) {
while (queue.size() == 0) {
queue.wait();
}
return queue.poll();
}
onFinish происходит в одном потоке (потоке выполнения асинхронного метода), поэтому совершенно корректно говорить «до».queue.size() != 0 могут возникнуть проблемы. Это значение не равно нулю только тогда, кода в очереди уже что-то есть. Что-то (объект «о») запихивается в очередь только внутри метода onFinish, который вызывается в потоке, в котором создается и инициализируется объект «о», только по завершению этой инициализации. Из этого следует, что queue.size() != 0 не может быть до инициализации объекта «о». queue.add(o) не атомарная и может случиться ситуация когда размер очереди уже увеличен, а переменная еще не добавлена в очередь? В этом случае, соглашусь насчет возможности увидеть queue в несогласованом состояни. (ох не зря я там вначале BlockingQueue ставил :) )while, когда хватает if-а. Главный поток продолжится только после queue.notify() и тогда уж точно queue.size() != 0.один поток может выполнять действия в одном порядке, а другой можеть видеть соответствующие изменения в памяти совсем в другом порядке.
Я уверен, что пока человек этого не понимает — его к написанию многопоточного кода на пушечный выстрел нельзя подпускать.
Преобразование асинхронных методов в синхронные с возвращением результата в Java