Java: создать фоновый поток для обновления переменной - PullRequest
0 голосов
/ 03 апреля 2019

* новичок в 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 ()? как он обрабатывает прерывания?

Ответы [ 3 ]

1 голос
/ 03 апреля 2019

Я думаю, что он работает следующим образом, но по какой-то причине дочерний поток висит даже после вызова 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;
                 }

             }
         });

     }
1 голос
/ 03 апреля 2019

Вы действительно можете использовать 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. Чтобы гарантировать, что factor будет корректно обновляться и параллельно считываться mainThread, вы можете использовать AtomicInteger, который предназначен для этого вида работы. Что касается отключения и прерывания, в конце вы должны сделать:

executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.SECONDS); // Time is arbitrary here. It depends your need

См. shutdown и awaitTermination, какой первый вызов имеет какую-либо разницу? для получения более подробной информации.

В вашем примере, когда я проверяю его, коэффициент не меняется, потому что вы ждете 5 секунд, а поток calcFactor ждет 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 только один раз, когда вы можете использовать countDownLatch для ожидания в потоке. Пример:

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-latch

1 голос
/ 03 апреля 2019

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. Обычно, перехватывайте команду do shutdown() после отключения сервера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...