* новичок в Java
в Main он вызывает processItems() перед итерацией по workItems. Проблема в том, что processItems() занимает много времени, скажем, 10 минут.
import java.lang.*;
import java.util.*;
import static java.util.stream.Collectors.*;
public class App {
private List<String> workItems;
private int factor = 0;
public App() {
workItems = new ArrayList<>();
//create some work in workItmes
workItems.add("apple");
workItems.add("oranges");
workItems.add("bananas");
}
public List<String> getWorkItems() {
return workItems;
}
public void calculateFactor() throws Exception{
// does some work which takes a long time
Thread.sleep(5*1000);
factor = 1;
}
public boolean evaluateItem(String item, int factor) {
// do some work based on item and current factor
System.out.println("Received: " + item + " " + factor);
return true;
}
public int getFactor() {
return factor;
}
public static void main(String[] args) throws Exception
{
App myApp = new App();
System.out.println("starting...");
int cnt = 0;
while (true) {
cnt++;
if (cnt > 5) {
break;
}
myApp.calculateFactor(); //<------- I dont want to call this here but in a background thread
int current_factor = myApp.getFactor();
List<Boolean> results = myApp.getWorkItems().stream().map(s -> myApp.evaluateItem(s, current_factor)).collect(toList());
System.out.println(results);
Thread.sleep(1*1000); // sleep a min
}
System.out.println("done");
}
}
Я хотел бы передать вызов, myApp.calculateFactor(), в основном фоновому потоку
Две вещи: этот фоновый поток должен иметь доступ к workItems и обновлять переменную, которая видна основному через getFactor()
Я смотрел на ExecutorService executorService= Executors.newSingleThreadExecutor();, но ни один из руководств, которые я, кажется, не предполагает, что он может видеть пространство имен родителя потока.
Кроме того, как будет выглядеть очистка фонового потока? просто вызвать shutdown()? как он обрабатывает прерывания?
@MasterChief обновил код
В основном методе вы заняты ожиданием while(true) { cnt++ ...Thread.sleep(1*1000)...}. Вы добавили это только для ожидания метода calculateFactor? Или по другой причине?
@Julien по другой причине, чтобы легко увидеть, что я ожидаю, что main завершится через 5 секунд (cnt равно 5 * 1 секунде) вместо 30 секунд, потому что дополнительные 5 секунд на итерацию от calcualteFactor, если calculateFactor происходит в фоновом потоке




java.lang.Thread В JavaDoc есть пример запуска системного потока. И JVM будет делать GC. Object.wait(), Thread.sleep(..) .. иметь возможность получать сигнал системного прерывания. Будьте осторожны с одновременным доступом к данным, приятно видеть volatile использование, чтобы предотвратить непоследовательное использование памяти. Ниже из JavaDoc
Например, поток, вычисляющий простые числа, превышающие заданное значение, можно записать следующим образом:
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
Затем следующий код создаст поток и запустит его выполнение:
PrimeThread p = new PrimeThread(143);
p.start();
И Executors.newSingleThreadExecutor() просто создайте ThreadPool только с 1 рабочим потоком с LinkedBlockingQueue очередью заданий. Используйте его как шаблон Singleton. Обычно дайте крючок сделать shutdown() после того, как сервер не работает.
Я думаю, что у меня это работает следующим образом, но по какой-то причине дочерний поток зависает даже после вызова shutdown() для исполнителя
private ExecutorService executor;
public App() {
workItems = new ArrayList<>();
//create some work in workItmes
workItems.add("apple");
workItems.add("oranges");
workItems.add("bananas");
executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
while (true) {
//Stopwatch stopwatch = Stopwatch.createStarted();
String threadName = Thread.currentThread().getName();
System.out.println("Hello " + threadName);
System.out.println("FROM THREAD: " + this.getWorkItems());
//this.incrementFactor(1);
try {
this.calculateFactor();
} catch (Exception e) {
//do nothing
}
try {
Thread.sleep(1*1000);
} catch (InterruptedException e) {
break;
}
}
});
}
Это потому, что вы никогда не выходите из цикла while(true). Оператор break во время Thread.sleep(1*100) никогда не достигается (он достигается только в случае прерывания (например, ctrl-c))
Прерывание @Julien произойдет только в том случае, если метод interrupt() вызывается в потоке. CTRL+C не вызовет прерывания.
@Holger хорошо, плохо. Я думал, что это так
Вы действительно можете использовать Executors.newSingleThreadExecutor() следующим образом:
public class App {
private List<String> workItems;
private AtomicInteger factor = new AtomicInteger(0);
public App() {
workItems = new ArrayList<>();
//create some work in workItmes
workItems.add("apple");
workItems.add("oranges");
workItems.add("bananas");
}
public List<String> getWorkItems() {
return workItems;
}
public void calculateFactor() throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
factor.set(1);
}
public boolean evaluateItem(String item, int factor) {
// do some work based on item and current factor
System.out.println("Received: " + item + " " + factor);
return true;
}
public AtomicInteger getFactor() {
return factor;
}
public static void main(String[] args) throws Exception {
App myApp = new App();
ExecutorService executorService = Executors.newSingleThreadExecutor();
System.out.println("starting...");
int cnt = 0;
while (true) {
cnt++;
if (cnt > 5) {
break;
}
executorService.submit(() -> {
try {
myApp.calculateFactor();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Error when calculating Factor");
}
});
int currentFactor = myApp.getFactor().get();
List<Boolean> results = myApp.getWorkItems().stream().map(s -> myApp.evaluateItem(s, currentFactor)).collect(toList());
System.out.println(results);
TimeUnit.SECONDS.sleep(1);
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.SECONDS);
System.out.println("done");
}
}
Здесь executorService запустит новый Runnable, который просто запустит ваш метод calculateFactor. Чтобы убедиться, что фактор будет правильно обновлен и прочитан mainThread параллельно, вы можете использовать AtomicInteger, предназначенный для такого рода заданий. О завершении работы и прерывании, в конце вы должны сделать:
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.SECONDS); // Time is arbitrary here. It depends your need
Подробнее см. shutdown и awaitTermination, какой первый вызов имеет какую-либо разницу?.
В вашем примере, когда я его тестирую, factor не меняется, потому что вы ждете 5 секунд, а поток calculateFactor ждет 5 секунд, поэтому мой результат:
starting...
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
done
Но если я поставлю, скажем, cnt> 10, у меня будет такой результат:
starting...
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 0
Received: oranges 0
Received: bananas 0
[true, true, true]
Received: apple 1
Received: oranges 1
Received: bananas 1
[true, true, true]
Received: apple 1
Received: oranges 1
Received: bananas 1
[true, true, true]
Received: apple 1
Received: oranges 1
Received: bananas 1
[true, true, true]
Received: apple 1
Received: oranges 1
Received: bananas 1
[true, true, true]
Received: apple 1
Received: oranges 1
Received: bananas 1
[true, true, true]
Надеюсь, это ответ на ваш вопрос
Добавление :, если вы хотите запустить calculateFactor только один раз, вы можете использовать защелка для ожидания в потоке. Пример :
public class App {
private List<String> workItems;
private AtomicInteger factor = new AtomicInteger(0);
private CountDownLatch countDownLatch = new CountDownLatch(1);
public App() {
workItems = new ArrayList<>();
//create some work in workItmes
workItems.add("apple");
workItems.add("oranges");
workItems.add("bananas");
}
public List<String> getWorkItems() {
return workItems;
}
public CountDownLatch getCountDownLatch() {
return countDownLatch;
}
public void calculateFactor() throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
factor.set(1);
countDownLatch.countDown();
}
public boolean evaluateItem(String item, int factor) {
// do some work based on item and current factor
System.out.println("Received: " + item + " " + factor);
return true;
}
public AtomicInteger getFactor() {
return factor;
}
public static void main(String[] args) throws Exception {
App myApp = new App();
ExecutorService executorService = Executors.newSingleThreadExecutor();
System.out.println("starting...");
executorService.submit(() -> {
try {
myApp.calculateFactor();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Error when calculating Factor");
}
});
myApp.getCountDownLatch().await();
int currentFactor = myApp.getFactor().get();
List<Boolean> results = myApp.getWorkItems().stream().map(s -> myApp.evaluateItem(s, currentFactor)).collect(toList());
System.out.println(results);
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.SECONDS);
System.out.println("done");
}
}
Выход будет:
starting...
Received: apple 1
Received: oranges 1
Received: bananas 1
[true, true, true]
done
Подробнее о CountDownlLatch: https://www.baeldung.com/java-countdown-защелка
Где используется
processItems()в main? Объект приложения не создан. Итак, как это используется в примере?