Свободные циклы вращения в потоках создают значительную нагрузку на процессор, что может отрицательно сказаться на других потоках.Помните, что больше потоков не всегда означает, что больше работы выполняется быстрее.
Требуется какой-то способ «дать» время, чтобы процессор мог лучше планировать другие потоки.
Поскольку вас интересует только вторая точность, для начала лучше использовать полусекунду sleep
.Это значительно сокращает время, необходимое каждому потоку на ЦП.
Лично, когда я имею дело с решениями, основанными на времени, я предпочитаю использовать API даты / времени, так как он обычно производит лучше и большенадежное решение, но это я.
В следующем примере просто запускается 10 потоков, каждый с 5-секундным таймаутом.Каждый поток спит в течение полсекунды, прежде чем пройти через предписанную ему логику
import java.time.Duration;
import java.time.Instant;
import java.util.Random;
public class Test {
public static void main(String[] args) throws InterruptedException {
new Test();
}
public Test() throws InterruptedException {
Random rnd = new Random();
for (int index = 0; index < 10; index++) {
Thread t = new Thread(new Timeout(5, "Cookie " + index));
t.start();
}
Thread.sleep(500);
}
public class Timeout implements Runnable {
private Duration duration;
private Instant startTime;
private String label;
public Timeout(int count, String label) {
duration = Duration.ofSeconds(count);
this.label = label;
}
@Override
public void run() {
long time = Long.MAX_VALUE;
try {
startTime = Instant.now();
while (true) {
Duration runTime = Duration.between(startTime, Instant.now());
Duration remainingTime = duration.minus(runTime);
// You could also use remainingTime.getSeconds() == 0, but it
// depends on your desired level of accuracy
if (remainingTime.isNegative()) {
System.out.println("Out of time");
return;
} else {
if (time != remainingTime.getSeconds()) {
time = remainingTime.getSeconds();
System.out.println(label + " " + duration.getSeconds() + "/" + time);
}
}
Thread.sleep(500);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
Это выдает результат, похожий на ...
Cookie 3 5/5
Cookie 4 5/5
Cookie 0 5/5
Cookie 1 5/5
Cookie 2 5/5
Cookie 6 5/5
Cookie 9 5/5
Cookie 5 5/5
Cookie 7 5/5
Cookie 8 5/5
Cookie 1 5/4
Cookie 5 5/4
Cookie 7 5/4
Cookie 6 5/4
Cookie 2 5/4
Cookie 0 5/4
Cookie 3 5/4
Cookie 4 5/4
Cookie 8 5/4
Cookie 9 5/4
//...
Cookie 5 5/1
Cookie 3 5/1
Cookie 0 5/1
Cookie 7 5/1
Cookie 1 5/1
Cookie 2 5/1
Cookie 6 5/1
Cookie 8 5/1
Cookie 4 5/1
Cookie 9 5/1
Cookie 5 5/0
Cookie 7 5/0
Cookie 4 5/0
Cookie 8 5/0
Cookie 0 5/0
Cookie 2 5/0
Cookie 3 5/0
Cookie 1 5/0
Cookie 6 5/0
Cookie 9 5/0
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Out of time
Другое решение может заключаться в использовании одного потока иList
«таймеров».Поток «отметит» таймеры, что позволит им определить, как долго они работают и истек ли он или нет, например ...
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) throws InterruptedException {
new Test();
}
public Test() throws InterruptedException {
List<Timeout> timers = new ArrayList<>(10);
for (int index = 0; index < 10; index++) {
timers.add(new Timeout(5, "Cookie " + index));
}
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Iterator<Timeout> it = timers.iterator();
while (it.hasNext()) {
Timeout timer = it.next();
timer.tick();
if (timer.isTimedOut()) {
it.remove();
}
}
Thread.yield();
if (timers.isEmpty()) {
return;
}
}
}
});
t.start();
Thread.sleep(500);
}
public class Timeout {
private Duration duration;
private Instant startTime;
private String label;
private Long lastTime;
private boolean timedOut;
public Timeout(int count, String label) {
duration = Duration.ofSeconds(count);
this.label = label;
}
public boolean isTimedOut() {
return timedOut;
}
public void tick() {
if (timedOut) {
return;
}
if (startTime == null) {
startTime = Instant.now();
}
Duration runTime = Duration.between(startTime, Instant.now());
Duration remainingTime = duration.minus(runTime);
// You could also use remainingTime.getSeconds() == 0, but it
// depends on your desired level of accuracy
if (remainingTime.isNegative()) {
System.out.println("Out of time");
timedOut = true;
} else {
if (lastTime == null || lastTime != remainingTime.getSeconds()) {
lastTime = remainingTime.getSeconds();
System.out.println(label + " " + duration.getSeconds() + "/" + lastTime);
}
}
}
}
}
Я мог бы даже добавить впара методов, чтобы вернуть «длительность» таймера, время работы и оставшееся время, но это я.
Недостатком этого является то, что если «основной» поток занимает слишком много времени,таймер может истечь до следующего цикла проверки.В приведенном выше примере я в основном позволил потоку работать максимально быстро (я добавил yield
, но это не мое любимое занятие) и просто перебрал список «таймеров», пока всетаймеры истекли.
Какое решение лучше?Зависит от ваших обстоятельств.Лично я стремлюсь к одному быстро работающему потоку (я использую Thread.sleep(5)
, но это только я), который может перебирать серию «дел, которые нужно сделать».В приведенном выше примере, поскольку мы полагаемся на решения, основанные на времени (а не на счетчики), даже если у нас есть некоторое отставание, мы все равно получаем (достаточно) точный результат