Вы можете организовать «однократное» выполнение фоновых вычислений, используя Future с ComputedMap. Будущее представляет задачу, которая вычисляет ценность. Будущее создается ComputedMap и в то же время передается ExecutorService для фонового выполнения. Исполнитель может быть настроен с вашей собственной реализацией ThreadFactory , которая создает потоки с низким приоритетом, например
class LowPriorityThreadFactory implements ThreadFactory
{
public Thread newThread(Runnable r) {
Tread t = new Thread(r);
t.setPriority(MIN_PRIORITY);
return t;
}
}
Когда значение необходимо, ваш высокоприоритетный поток затем извлекает будущее из карты и вызывает метод get () для получения результата, ожидая его вычисления в случае необходимости. Чтобы избежать инверсии приоритетов , добавьте в задачу дополнительный код:
class HandlePriorityInversionTask extends FutureTask<ResultType>
{
Integer priority; // non null if set
Integer originalPriority;
Thread thread;
public ResultType get() {
if (!isDone())
setPriority(Thread.currentThread().getPriority());
return super.get();
}
public void run() {
synchronized (this) {
thread = Thread.currentThread();
originalPriority = thread.getPriority();
if (priority!=null) setPriority(priority);
}
super.run();
}
protected synchronized void done() {
if (originalPriority!=null) setPriority(originalPriority);
thread = null;
}
void synchronized setPriority(int priority) {
this.priority = Integer.valueOf(priority);
if (thread!=null)
thread.setPriority(priority);
}
}
Это заботится о повышении приоритета задачи до приоритета потока, вызывающего get()
, если задача еще не выполнена, и возвращает приоритет оригиналу, когда задача завершается, обычно или иначе. (Короче говоря, код не проверяет, действительно ли приоритет выше, но его легко добавить.)
Когда высокоприоритетная задача вызывает get (), будущее еще не может начаться. Возможно, вы захотите избежать этого, установив большую верхнюю границу для числа потоков, используемых службой executor, но это может быть плохой идеей, поскольку каждый поток может работать с высоким приоритетом, потребляя столько процессоров, сколько он мог раньше ОС выключает это. Пул, вероятно, должен быть того же размера, что и количество аппаратных потоков, например Размер бассейна до Runtime.availableProcessors()
. Если задача еще не начала выполняться, вместо того, чтобы ждать, пока исполнитель не запланирует ее (что является формой инверсии приоритетов, поскольку ваш поток с высоким приоритетом ожидает завершения потоков с низким приоритетом), вы можете отменить его с текущего исполнителя и повторно отправьте его на исполнителя, выполняющего только высокоприоритетные потоки.