У меня есть один производитель и многие потребители.
- производитель работает быстро и генерирует много результатов.
- токены с одинаковым значением необходимо обрабатывать последовательно
- токены с разными значениями должны обрабатываться параллельно
- создание новых Runnables было бы очень дорогостоящим, а также производственный код мог бы работать с 100k токенов (для создания Runnable мне нужно передать конструктору некоторый сложный объект для сборки)
Могу ли я достичь тех же результатов с помощью более простого алгоритма? Вложение блока синхронизации с реентерабельной блокировкой кажется немного неестественным. Есть ли какие-либо условия гонки, которые вы могли бы заметить?
Обновление: второе решение, которое я нашел, работало с 3 коллекциями. Один для кэширования результатов производителя, второй очереди блокировки и третий, используя список для отслеживания в выполняемых задачах. Снова немного сложно.
Моя версия кода
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
public class Main1 {
static class Token {
private int order;
private String value;
Token() {
}
Token(int o, String v) {
order = o;
value = v;
}
int getOrder() {
return order;
}
String getValue() {
return value;
}
}
private final static BlockingQueue<Token> queue = new ArrayBlockingQueue<Token>(10);
private final static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<String, Object>();
private final static ReentrantLock reentrantLock = new ReentrantLock();
private final static Token STOP_TOKEN = new Token();
private final static List<String> lockList = Collections.synchronizedList(new ArrayList<String>());
public static void main(String[] args) {
ExecutorService producerExecutor = Executors.newSingleThreadExecutor();
producerExecutor.submit(new Runnable() {
public void run() {
Random random = new Random();
try {
for (int i = 1; i <= 100; i++) {
Token token = new Token(i, String.valueOf(random.nextInt(1)));
queue.put(token);
}
queue.put(STOP_TOKEN);
}catch(InterruptedException e){
e.printStackTrace();
}
}
});
ExecutorService consumerExecutor = Executors.newFixedThreadPool(10);
for(int i=1; i<=10;i++) {
// creating to many runnable would be inefficient because of this complex not thread safe object
final Object dependecy = new Object(); //new ComplexDependecy()
consumerExecutor.submit(new Runnable() {
public void run() {
while(true) {
try {
//not in order
Token token = queue.take();
if (token == STOP_TOKEN) {
queue.add(STOP_TOKEN);
return;
}
System.out.println("Task start" + Thread.currentThread().getId() + " order " + token.getOrder());
Random random = new Random();
Thread.sleep(random.nextInt(200)); //doLongRunningTask(dependecy)
lockList.remove(token.getValue());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}});
}
}}