0

*new to Java

in Main, it calls processItems() before iterating through workItems. The problem is processItems() takes a long time, say 10min

 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");
     }
 }

I would like to farm out the call, myApp.calculateFactor(), in main to a background thread

Two things: this background thread needs to be access workItems and update a variable which is visible to main via getFactor()

I was looking at ExecutorService executorService= Executors.newSingleThreadExecutor(); but none of the tutorials i've seem suggest it can see the namespace of the thread's parent.

Also, how would cleaning up of the backgroud thread look like? just call shutdown()? how does it handle interrupts?

ealeon
  • 12,074
  • 24
  • 92
  • 173
  • Where is `processItems()` used in main? There is not object of App instantiated. So how it is used in the example? – Master Chief Apr 03 '19 at 07:46
  • @MasterChief updated the code – ealeon Apr 03 '19 at 07:51
  • In main method, you are making a busy waiting `while(true) { cnt++ ...Thread.sleep(1*1000)...}`. Did you add this only to wait on calculateFactor method ? Or for another reason ? – Julien Apr 03 '19 at 08:01
  • @Julien for another reason, to easily see that i expect main to finish in 5 seconds (cnt is 5 * 1 sec) instead of 30s because extra 5s per iteration from calcualteFactor, if calculateFactor occurs in a background thread – ealeon Apr 03 '19 at 08:08

3 Answers3

1

java.lang.Thread JavaDoc has an example of how to run a system thread. And, JVM would do GC. Object.wait(), Thread.sleep(..) .. have the ability to get the system interrupt signal. Be care of concurrent data accessing, it's good to see volatile usage to prevent some memory inconsistently. Below from the JavaDoc

For example, a thread that computes primes larger than a stated value could be written as follows:

class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }

The following code would then create a thread and start it running:

PrimeThread p = new PrimeThread(143);
p.start();

And, Executors.newSingleThreadExecutor() just create a ThreadPool with only 1 work thread with a LinkedBlockingQueue job queue. Use it as the Singleton Pattern. Usually, give a hook do shutdown() after server down.

diorch
  • 51
  • 5
1

I think I have it working as the following but for some reason child thread is hanging on even after calling shutdown() for executor

     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;
                 }

             }
         });

     }
ealeon
  • 12,074
  • 24
  • 92
  • 173
  • It is because you never leave the while(true) loop. The break statement during `Thread.sleep(1*100)` is never reached (it is only reached if there is an interruption (ctrl-c for example)) – Julien Apr 03 '19 at 09:30
  • 2
    @Julien interruption will only happen if the method `interrupt()` is invoked on the thread. CTRL+C will not cause interruption. – Holger Apr 03 '19 at 11:06
  • @Holger ok my bad. I thought that was the case – Julien Apr 03 '19 at 11:11
1

You can indeed use Executors.newSingleThreadExecutor() like this :

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");
    }
}

Here the executorService will run a new Runnable which just run your method calculateFactor. To ensure that factor will be updated correctly and read by the mainThread in parallel, you can use an AtomicInteger which is dedicated for this kind of job. About shutdown and interrupt, at the end you should do :

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

See shutdown and awaitTermination which first call have any difference? for more details.

In your example, when I test it, factor does not change because you wait 5 seconds and the thread calculateFactor wait 5 seconds so my result is :

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

But if I put let's say cnt>10 I have this result :

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]

Hope it answers your question

Addition : if you want to run calculateFactor only once you can use countDownLatch to wait on the Thread. Example :

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");
    }
}

Output will be :

starting...
Received: apple 1
Received: oranges 1
Received: bananas 1
[true, true, true]
done

More details on CountDownlLatch : https://www.baeldung.com/java-countdown-latch

Julien
  • 126
  • 4