【开发百宝箱系列】- FutrueTask 使用

/**
* 当一个线程需要等待另一个线程把某个任务执行完后它才能继续执行,此时可以使用FutureTask。
* 假设有多个线程执行若干任务,每个任务最多只能被执行一次。
* 当多个线程试图同时执行同一个任务时,只允许一个线程执行任务,其他线程需要等待这个任务执行完后才能继续执行
*
* @author gerry pang
* @since 2020年10月21日 下午4:33:09
*/
public class FutureTaskDemo {

      private final Map<String, Future<String>> taskCache = new  ConcurrentHashMap<>();
      public String executeTask(String taskName) {
            Future<String> future = taskCache.get(taskName);
            if (future == null) {
                  Callable<String> task = new Callable<String>() {
                        @Override
                        public String call() throws Exception {
                              System.out.println("==> start  taskName:"+taskName);
                              Thread.sleep(new Random().nextInt(1000));
                              System.out.println("==> end  taskName:"+taskName);
                              return taskName;
                        }
                  };

                  FutureTask<String> futureTask = new  FutureTask<String>(task);
                  future = taskCache.putIfAbsent(taskName, futureTask);
                  if (future == null) {
                        future = futureTask;
                        futureTask.run();
                  }
            }
            try {
                  return future.get();
            } catch (InterruptedException e) {
                  e.printStackTrace();
            } catch (ExecutionException e) {
                  e.printStackTrace();
            }
            return taskName;
      }

      public static void main(String[] args) {
            FutureTaskDemo futureTaskDemo = new FutureTaskDemo();
            futureTaskDemo.executeTask("123");
            futureTaskDemo.executeTask("123");
            futureTaskDemo.executeTask("456");
            futureTaskDemo.executeTask("456");
            futureTaskDemo.executeTask("000");
            futureTaskDemo.executeTask("123");
      }
}

执行结果

==> start taskName:123
==> end taskName:123
123
123
==> start taskName:456
==> end taskName:456
456
456
==> start taskName:000
==> end taskName:000
000
123

参考资料

  • 《并发编程的艺术》 10.4.2 FutrueTask的使用

【开发百宝箱系列】- CountDownLunch标准用法

CountDownLunch 标准用法1:开始+结束信号

class Driver {
  void main() throws InterruptedException {
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(N);

    for (int i = 0; i < N; ++i) {
      // create and start threads
      new Thread(new Worker(startSignal, doneSignal)).start();
    }

    doSomethingElse();            // don&#039;t let run yet
    startSignal.countDown();      // let all threads proceed
    doSomethingElse();

    doneSignal.await();           // wait for all to finish
  }
}

class Worker implements Runnable {

  private final CountDownLatch startSignal;
  private final CountDownLatch doneSignal;

  Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
    this.startSignal = startSignal;
    this.doneSignal = doneSignal;
  }

  public void run() {
    try {
      startSignal.await();
      doWork();
      doneSignal.countDown();
    } catch (InterruptedException ex) {
        // error
    } // return;
  }

  void doWork() { ... }

}

CountDownLatch 标准用法2:基于线程池

class Driver2 {
   void main() throws InterruptedException {
     CountDownLatch doneSignal = new CountDownLatch(N);
     ExecutorService e = Executors.newFixedThreadPool(N);

     for (int i = 0; i < N; ++i) {
       // create and start threads
       e.execute(new WorkerRunnable(doneSignal, i));
     }

     doneSignal.await();           // wait for all to finish
   }
}

class WorkerRunnable implements Runnable {
   private final CountDownLatch doneSignal;
   private final int i;

   WorkerRunnable(CountDownLatch doneSignal, int i) {
     this.doneSignal = doneSignal;
     this.i = i;
   }

   public void run() {
     try {
       doWork(i);
       doneSignal.countDown();
     } catch (InterruptedException ex) {
        // error
     } // return;
   }

   void doWork() { ... }
}