Безопасно ли помещать обратный вызов без состояния в 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 4

1 ответ:

Ответ заключается в том, что это зависит от обстоятельств.

Если вы никогда не изменяете экземпляр Person после этого, то это гарантировано. Существует отношение случается-до, как указано в документах :

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

Однако я все равно не стал бы этого делать. Если что-то изменило экземпляр Person после того, как обратный вызов был помещен в очередь, , то нет никаких гарантий. Можно сказать, что регистратор гарантированно печатает состояние, которое является по крайней мере таким же актуальным, как и при добавлении в очередь. Таким образом, потребитель увидит эти модификации:
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 в данный момент.