简体   繁体   中英

How can I schedule some work in n threads separately

Lets say I have n threads concurrently taking values from a shared queue:

public class WorkerThread implements Runnable{
        private BlockingQueue queue;
        private ArrayList<Integer> counts = new ArrayList<>();
        private int count=0;
        public void run(){
            while(true) {
                queue.pop();
                count++;
            }
        }
}

Then for each thread, I want to count every 5 seconds how many items it has dequeued, and then store it in its own list (counts) I've seen here Print "hello world" every X seconds how you can run some code every x seconds:

Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask(){
    @Override
    public void run(){
        counts.add(count);
        count = 0
    }
}, 0, 5000);

The problem with this is that I can't access count variable and the list of counts unless they are static. But I don't want them to be static because I don't want the different threads to share those variables.

Any ideas of how to handle this?

I don't think it's possible to use scheduled execution for you case(neither Timer nor ScheduledExecutorService ), because each new scheduled invocation will create a new tasks with while loop. So number of tasks will increase constantly.

If you don't need to access this list of counts in runtime i would suggest something like this one:

  static class Task implements Runnable {
    private final ThreadLocal<List<Integer>> counts = ThreadLocal.withInitial(ArrayList::new);
    private volatile List<Integer> result = new ArrayList<>();
    private BlockingQueue<Object> queue;

    public Task(BlockingQueue<Object> queue) {
      this.queue = queue;
    }

    @Override
    public void run() {
      int count = 0;
      long start = System.nanoTime();
      try {
        while (!Thread.currentThread().isInterrupted()) {
          queue.take();
          count++;
          long end = System.nanoTime();
          if ((end - start) >= TimeUnit.SECONDS.toNanos(1)) {
            counts.get().add(count);
            count = 0;
            start = end;
          }
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
      // the last value
      counts.get().add(count);
      // copy the result cause it's not possible
      // to access thread local variable outside of this thread
      result = counts.get();
    }

    public List<Integer> getCounts() {
      return result;
    }
  }

  public static void main(String[] args) throws Exception {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    BlockingQueue<Object> blockingQueue = new LinkedBlockingQueue<>();
    Task t1 = new Task(blockingQueue);
    Task t2 = new Task(blockingQueue);
    Task t3 = new Task(blockingQueue);
    executorService.submit(t1);
    executorService.submit(t2);
    executorService.submit(t3);

    for (int i = 0; i < 50; i++) {
      blockingQueue.add(new Object());
      Thread.sleep(100);
    }
    // unlike shutdown() interrupts running threads
    executorService.shutdownNow();
    executorService.awaitTermination(1, TimeUnit.SECONDS);

    System.out.println("t1 " + t1.getCounts());
    System.out.println("t2 " + t2.getCounts());
    System.out.println("t3 " + t3.getCounts());

    int total = Stream.concat(Stream.concat(t1.getCounts().stream(), t2.getCounts().stream()), t3.getCounts().stream())
        .reduce(0, (a, b) -> a + b);
    // 50 as expected
    System.out.println(total);
  }
  • Why not a static AtomicLong?
  • Or the WorkerThread(s) can publish that they poped to the TimerTask or somewhere else? And the TimerTask reads that info?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM