Мне нужны отдельные потоки для каждой запущенной задачи, поэтому изменение архитектуры не вариант.
Если это правда (например, вызов внешней блокирующей функции), то создайте для них отдельные потоки и запустите их. Вы не можете создать пул потоков с ограниченным числом потоков, так как блокирующая функция в одном из потоков предотвратит попадание в него любого другого исполняемого объекта и не принесет много пользы при создании пула потоков с одним потоком на задачу.
Я попытался сделать размер своего threadPool равным Runtime.getRuntime(). AvailableProcessors(), который пытался запустить все 500 потоков, но позволил выполнить только 8 (4xhyperthreading) из них.
Когда вы передаете создаваемые вами объекты Thread в пул потоков, он видит только то, что они реализуют Runnable
. Поэтому он будет запускать каждый Runnable
до завершения. Любой цикл, который останавливает возврат метода run()
, не позволит запустить следующую поставленную в очередь задачу; например:
public static void main (String...args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; ++i) {
final int task = i;
executor.execute(new Runnable () {
private long lastRunTime = 0;
@Override
public void run () {
for (int iteration = 0; iteration < 4; )
{
if (System.currentTimeMillis() - this.lastRunTime > TIME_OUT)
{
// do your work here
++iteration;
System.out.printf("Task {%d} iteration {%d} thread {%s}.\n", task, iteration, Thread.currentThread());
this.lastRunTime = System.currentTimeMillis();
}
else
{
Thread.yield(); // otherwise, let other threads run
}
}
}
});
}
executor.shutdown();
}
распечатывает:
Task {0} iteration {1} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {1} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {2} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {2} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {3} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {3} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {4} thread {Thread[pool-1-thread-1,5,main]}.
Task {2} iteration {1} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {4} thread {Thread[pool-1-thread-2,5,main]}.
Task {3} iteration {1} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {2} thread {Thread[pool-1-thread-1,5,main]}.
Task {3} iteration {2} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {3} thread {Thread[pool-1-thread-1,5,main]}.
Task {3} iteration {3} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {4} thread {Thread[pool-1-thread-1,5,main]}.
...
показывая, что первые задачи (размер пула потоков) выполняются до завершения до того, как будут запланированы следующие задачи.
Что вам нужно сделать, так это создать задачи, которые выполняются какое-то время, а затем позволить другим задачам выполняться. То, как вы их структурируете, зависит от того, чего вы хотите достичь.
- хотите ли вы, чтобы все задачи выполнялись одновременно, все ждали минуту, затем снова все выполнялись одновременно, или задачи не синхронизируются друг с другом
- действительно ли вы хотели, чтобы каждая задача выполнялась с интервалом в одну минуту
- независимо от того, потенциально ли ваши задачи блокируются или нет, и поэтому действительно требуют отдельных потоков
- какое поведение ожидается, если задача блокируется дольше ожидаемого окна для запуска
- какое поведение ожидается, если задача блокируется дольше, чем частота повторения (блокируется более одной минуты)
В зависимости от ответов на них для координации задач может использоваться некоторая комбинация ScheduledExecutorService, семафоров или мьютексов. Простейшим случаем являются неблокирующие, несинхронные задачи, и в этом случае используйте ScheduledExecutorService напрямую для запуска ваших runnables каждую минуту.
person
Pete Kirkham
schedule
19.05.2010