Вызов метода только один раз с несколькими потоками - PullRequest
3 голосов
/ 23 июня 2019

У меня есть веб-приложение, которое обслуживает много запросов одновременно.В одном из методов API приложения у меня есть метод - methodA ().В этом методе у меня есть вызов другого метода - doSomething ().У меня есть сценарий, в котором я хочу, чтобы первый вызов methodA () запускал метод doSomething () в отдельном потоке, но в настоящее время, если был вызван другой вызов methodA (), не запускайте doSomething () (потому что он все еще выполняется другим потоком) и просто продолжите с методом methodA ().

methodA() {
 .
 .
 doSomething() // In a new thread
 .
 .
}

Я рассмотрел использование атомарного логического значения в качестве флага, но я не уверен, что этолучшая идея.

    private final AtomicBoolean isOn = new AtomicBoolean(false);
    methodA() {
        .
        .
        if (isOn.compareAndSet(false, true)) {
                     Runnable doSomethingRunnableTask = () -> { 
                         doSomething(); };
                     Thread t1 = new Thread(doSomethingRunnableTask);
                     t1.start();
                     isOn.set(false);
        } 

Спасибо!

Ответы [ 3 ]

0 голосов
/ 23 июня 2019

Я думаю, вы могли бы использовать ReentrantLock, и это tryLock метод.Из документов ReentrantLock::tryLock

Получает блокировку, только если она не удерживается другим потоком во время вызова.

Если текущий поток ужеудерживает эту блокировку, то счетчик удержаний увеличивается на единицу, и метод возвращает значение true.

Если блокировка удерживается другим потоком, этот метод немедленно вернется со значением false.

Таким образом, вы можете создать такую ​​блокировку в вашем сервисе как поле, чтобы потоки, которые вызывают вашу methodA, поделились ею, а затем:

public class MyService {
    private ReentrantLock reentrantLock = new ReentrantLock();

    public void methodA() {
        if(reentrantLock.tryLock()) {
            doSomething();
            reentrantLock.unlock();
        }
    }
}

EDIT : Здесь блокировка будет удерживатьсявызвав Thread, этот поток будет ожидать завершения переданной задачи, а затем разблокирует блокировку:

public class MyService {
    private ReentrantLock reentrantLock = new ReentrantLock();

    private ExecutorService pool = Executors.newCachedThreadPool();

    public void methodA() {
        if(reentrantLock.tryLock()) {
            Future<?> submit = pool.submit(() -> doSomething()); // you can submit your invalidateCacheRunnableTask runnable here.
            try {
                submit.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }
    }
}

Также помните, что в этом примере я использовал threadPool, поэтому этот пул необходимо соответствующим образом закрыть.

0 голосов
/ 24 июня 2019

Вместо того, чтобы использовать какую-то явную блокировку, я бы предложил вам использовать очередь блокировки . Преимущество, которое я вижу, состоит в том, что вам не нужно будет повторно создавать поток, если / когда это необходимо. Вам нужно будет создать поток только один раз, который будет обрабатывать только все doSomething .

Сценарий:

Когда вызывается methodA , он помещает необходимую информацию для вашего выделенного потока в BlockingQueue и продолжает работу. Выделенный поток будет опрашивать информацию из BlockingQueue (блокировка в пустой очереди). Когда некоторая информация поступает в очередь, она запускает ваш метод doSomething .

BlockingQueue<Info> queue;

methodA() {
    //...
    queue.add(info);
    // non-blocking, keeps going    
}


void dedicatedThread(){
    for(;;) {
        //Blocks until some work is put in the queue
        Info info = queue.poll(); 
        doSomething(info);
    }

}

Примечание: Я предположил, что тип Info содержит необходимую информацию для метода doSomething . Однако, если вам не нужно делиться какой-либо информацией, я бы предложил вам использовать вместо этого семафор. В этом случае methodA поместит заявки в семафор, а выделенный поток попытается нарисовать билеты, блокируя их до тех пор, пока не будут получены некоторые билеты.

0 голосов
/ 23 июня 2019

Вы можете использовать ReentrantLock . Блокировка будет разрешать только один поток за раз, и его метод tryLock () немедленно вернется с истиной или ложью в зависимости от того, была ли получена блокировка.

ReentrantLock lock = new ReentrantLock();

methodA() {
    ...
    if (lock.tryLock()) {
        try {
            doSomething();
        } finally {
            lock.unlock();
        }
    }
    ...
}

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

AtomicBoolean flag = new AtomicBoolean();

methodA() {
    ...
    if (flag.compareAndSet(false, true)) {
        // execute in another thread / executor
        new Thread(() -> {
            try {
                doSomething();
            } finally {
                // unlock within the executing thread
                // calling thread can continue immediately
                flag.set(false);
            }
        }).start();
    }
    ...
}
...