Java:创建后台线程以更新变量

时间:2019-04-03 07:23:43

标签: java multithreading java-8

* Java的新手

在Main中,它在遍历workItems之前调用processItems()。 问题是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()对main可见

我在看ExecutorService executorService= Executors.newSingleThreadExecutor();,但似乎没有一个教程暗示它可以看到线程的父级名称空间。

此外,如何清理背景螺纹?只是调用shutdown()?它如何处理中断?

3 个答案:

答案 0 :(得分:1)

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()仅创建一个只有1个工作线程且工作队列为LinkedBlockingQueue的ThreadPool。用作单例模式。通常,在服务器宕机后给钩子做一个shutdown()

答案 1 :(得分:1)

我认为我可以按以下方式工作,但是由于某种原因,即使在为执行者调用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;
                 }

             }
         });

     }

答案 2 :(得分:1)

您确实可以像这样使用 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,该Runnable仅运行您的方法calculateFactor。为了确保 factor 可以正确更新并由mainThread并行读取,可以使用专用于此类作业的AtomicInteger。关于关机和中断,最后应该做:

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

有关更多详细信息,请参见shutdown and awaitTermination which first call have any difference?

在您的示例中,当我对其进行测试时,因数不变,因为您等待了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,则可以使用 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