Java FutureTask - многопоточный вызов get () - PullRequest
0 голосов
/ 28 июня 2018

У меня есть два следующих метода в классе:

private MyDef myDef;
private FutureTask<MyDef> defFutureTask;

public synchronized void periodEviction() {
       myDef = null;
}

    public MyDef loadMyItems() {

    // if it's not ready use a future - it will block until the results are ready
    if (this.myDef == null) { // this will still not be thread safe
        Callable<MyDef> callableDef = ()->{ return this.loadFromDatabase(); };
        FutureTask<MyDef> defTask = new FutureTask<>(callableDef);
        this.defFutureTask = defTask;
        defFutureTask.run();            
    }        

    try {
        // wait until's it's ready
        this.myDef = this.qDefFuture.get();                     
    } catch(InterruptedException e) {
        log.error(this.getClass(), "Interrupted whilst getting future..");
    } catch(ExecutionException e) {
        log.error(this.getClass(), "Error when executing callable future");
    }         
    return this.myDef; 
}

Я хотел сделать следующее:

1) Выполните удаление кэша, используя periodEviction() каждый час или около того.

2) В противном случае используйте кэшированное значение после завершения загрузки дБ.

Мне кажется, я неправильно понял будущее Java, так как не смог ответить на вопрос: «Что происходит, когда все потоки A, B и C вызывают loadMyItems() одновременно?»

Так значит ли это, что без исполнителя, эта реализация все еще не поточно-безопасна?

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Еще более простой подход состоит в том, чтобы вообще не кэшировать объект, а просто сохранить Future.

private CompletableFuture<MyDef> defFuture;

public synchronized void periodEviction() {
    // evict by triggering the request anew
    defFuture = CompletableFuture.supplyAsync(this::loadFromDatabase);
}

public synchronized Optional<MyDef> loadMyItems() {
    try {
        return Optional.of(this.defFuture.get());
    } catch(InterruptedException e) {
        log.error(this.getClass(), "Interrupted whilst getting future..");
    } catch(ExecutionException e) {
        log.error(this.getClass(), "Error when executing callable future");
    }         
    return Optional.empty();
}

С оговоркой, что это будет запускать запрос к базе данных каждый период исключения, а не по требованию.

0 голосов
/ 28 июня 2018

Очень простой подход - объявить loadMyItems как synchronized. Но если у класса есть другие методы, которые обращаются к myDef, вы должны также объявить эти synchronized. Иногда это приводит к очень грубой блокировке и снижению производительности.

Если вы ищете самый чистый / быстрый код, вместо объявления periodEviction как synchronized, объявите myDef как AtomicReference:

private final AtomicReference<MyDef> myDef = new AtomicReference<>();

Тогда тело periodEviction есть:

synchronized (myDef) {
    myDef.set(null);
}

А тело loadMyItems это:

synchronized (myDef) {
   if (myDef.get() == null) {
        // perform initialization steps, ending with:
        myDef.set(this.qDefFuture.get());
   }
   return myDef.get();
}

Если многие потоки вызывают loadMyItems одновременно, myDef будет когда-либо инициализирован только один раз, и все они получат один и тот же объект, возвращенный (если каким-то образом не вызовет periodEviction в середине).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...