Безопасно ли помещать обратный вызов без состояния в BlockingQueue?
У меня есть следующий довольно простой интерфейс обратного вызова и класс POJO:
public interface Action{
public void doAction();
}
public class Person{
private String name;
private String address;
//...etc
//GET, SET, toString
}
И я собираюсь использовать его следующим образом:
public class ActionExecutor{
private static final Logger logger = LogManager.getLogger(ActionExecutor.class);
private final BlockingQueue<Action> blockingQueue = new LinkedBlockingQueue(2000);
public void execute(final Person p){
//modify state of p in some way
blockingQueue.put(new Action(){
public void doAction(){
logger.info("Execution started: " +p.toString );
//do some other job
});
}
}
BlockingQueue
здесь используется для реализации производитель-потребитель.
Вопрос: гарантируется ли, что поток-потребитель, выполняющий действие из BlockingQueue
, напишет правильное сообщение журнала? То есть он наблюдает правильное состояние Person
? Но я не совсем уверен в этом.
Я думаю, что нет, это не гарантировано, так как есть ничего не происходит до заказа между модификациями, сделанными производителем и чтением производителем.
1 ответ:
Ответ заключается в том, что это зависит от обстоятельств.
Если вы никогда не изменяете экземпляр Person после этого, то это гарантировано. Существует отношение случается-до, как указано в документах :
Однако я все равно не стал бы этого делать. Если что-то изменило экземпляр Person после того, как обратный вызов был помещен в очередь, , то нет никаких гарантий. Можно сказать, что регистратор гарантированно печатает состояние, которое является по крайней мере таким же актуальным, как и при добавлении в очередь. Таким образом, потребитель увидит эти модификации:Эффекты согласованности памяти: как и в других параллельных коллекциях, действия в потоке перед помещением объекта в BlockingQueue произойдет-до действий, следующих за доступом или удалением этого элемент из BlockingQueue в другом нитка.
Но не те, что были сделаны после этого. И так как вы держитеpublic void execute(final Person p){ //modify state of p in some way blockingQueue.put(new Action(){
p
вокруг после прохождения его для Объектаnew Action()
я бы этого избегал. Вместо этого я бы сделал что-то вроде этого:public void execute(final Person p){ //modify state of p in some way blockingQueue.put(new Action() { final String executionName = p.toString(); public void doAction(){ logger.info("Execution started: " + executionName); //do some other job }); }
Теперь состояние фиксируется в конечной переменной. Потребитель увидит эту ценность, независимо от того, каково состояние
p
в данный момент.