2017-09-13 3490 views
2

我想用泛型(我第一次嘗試使用泛型)和使用ExecutorService來實現「TaskExecutor」。帶參數的Callable lambda表達式

這裏是我的 「的TaskExecutor」 類:

public class ExecuteAlerterTask<T> { 
    public List<T> process(String executorName, Callable<T> task) throws ExecutionException, InterruptedException { 
     final ThreadFactory threadFactory = new ThreadFactoryBuilder() 
       .setNameFormat(executorName + "-%d") 
       .setDaemon(true) 
       .build(); 
     ExecutorService executor = Executors.newFixedThreadPool(10, threadFactory); 
     Collection<Future<T>> futures = new ArrayList<>(); 
     IntStream.range(1, 10).forEach(i -> { 
      Future<T> future = executor.submit(task); 
      futures.add(future); 
     }); 

     List<T> result = new ArrayList<>(); 
     for (Future<T> f : futures) { 
      result.add(f.get()); 
     } 
     executor.shutdown(); 
     return result; 
    } 
} 

這是我的方式來運行它:

@Test 
public void process() throws Exception { 
    Callable<String> callable =() -> "Do something on "; 
    ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>(); 
    List<String> result = executeAlerterTask.process("TaskName", callable); 
    result.forEach(System.out::println); 
} 

這裏是我的問題: 如何寫我的贖回,這會接受論點i at a line:

Future<T> future = executor.submit(task); 

E.g.所需結果將是:

Do something on 1 
Do something on 3 
Do something on 7 
Do something on 2 
<...etc...> 

如果別的東西是錯我的代碼 - 請讓我知道。上述

編輯

刪除工具可贖回

代碼是我真正想要做的抽象

  • IntRange真的被設置批次,其中從SQL獲取數據的。可調用 確實實現瞭如何處理這些SQL批處理的邏輯。

EDIT2

所有的建議後,我已經下面的解決方案:

public class ExecuteAlerterTask<T> { 
    public List<T> process(String executorName, Collection<Callable<T>> task) throws ExecutionException, InterruptedException { 
     final ThreadFactory threadFactory = new ThreadFactoryBuilder() 
       .setNameFormat(executorName + "-%d") 
       .setDaemon(true) 
       .build(); 
     ExecutorService executor = Executors.newFixedThreadPool(10, threadFactory); 
     Collection<Future<T>> futures = executor.invokeAll(task); 

     List<T> result = new ArrayList<>(); 
     for (Future<T> f : futures) { 
      result.add(f.get()); 
     } 
     executor.shutdown(); 
     return result; 
    } 
} 

而且方式來運行它:

@Test 
    public void process() throws Exception { 
     Collection<Callable<String>> tasks = new ArrayList<>(); 
     IntStream.range(1, 10).forEach(i -> { 
      tasks.add(new Task(i).callable); 
     }); 

     ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>(); 
     List<String> result = executeAlerterTask.process("TaskName", tasks); 
     result.forEach(System.out::println); 
    } 

    private class Task { 
     private int i; 
     private Callable<String> callable =() -> "Doing something on i: " + i; 
     private Task(int i) { 
      this.i = i; 
     } 
    } 

EDIT3

運行它更簡單的方法:

@Test 
    public void process() throws Exception { 
     Collection<Callable<String>> tasks = new ArrayList<>(); 
     IntStream.range(1, 10).forEach(i -> { 
      tasks.add(() -> "Do something on i: " + i * 2); 
     }); 

     ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>(); 
     List<String> result = executeAlerterTask.process("TaskName", tasks); 
     result.forEach(System.out::println); 
    } 

我想我最終的解決方案非常滿意。謝謝大家!

+3

爲什麼你在不使用'Callable'的時候不使用它? – RealSkeptic

+0

@RealSkeptic好了。這回答瞭如何擺脫T調用的問題() – lapkritinis

+0

它幾乎看起來像要顯示在哪個線程上執行工作,但是您已經以完全反向的方式編寫了代碼。 – Kayaman

回答

1

首先,你根本不需要call(),你也不需要在這個類中實現Callable<T>,因爲你從不使用它。

要創建一個可調用你想要的方式,你couldd做

Callable<String> task; // from the parameter 
int i; // from the loop 
Callable<String> wrapper =() -> { return task.call() + " on " + i; } 
executor.submit(wrapper); 

你基本上給拉姆達外部變量i作爲參數。

+0

我看到它會返回我所問的內容。只是想這個解決方案是否適用於我的「真實的東西」 – lapkritinis

+0

但是這會將「業務邏輯」移到TaskExecutor中 - 這是我想要避免的 – lapkritinis

+1

是的,但是將TaskExecutor中的'i'參數注入到您的業務中邏輯聽起來像你想要做的。我可能誤解了你的問題;也許你應該發佈一個「想要的運行方式」而不是「期望的結果」。 – daniu

1

這是不可能的。如果這是一個lambda,你不能將一個變量傳遞給可調用對象。在另一方面,你可以使用實現Callable並具有可變二傳手自己的特定對象:

public class CallableWithParam implements Callable<String> { 

    // protected for subclassing call() 
    // volatile for multi-threaded reasons 
    protected volatile int param = 0; 

    public void setParam(int param) { 
     this.param = param; 
    } 

    @Override 
    public String call() { 
     return "my param is: " + param; 
    } 

} 

用法:

@Test 
public void process() throws Exception { 
    CallableWithParam callable = new CallableWithParam() { 
    @Override 
     public String call() { 
      // an anonymous inner class is almost a lambda ;) 
      return "my param is: " + param + "in subclass"; 
     } 
    }; 
    callable.setParam(3); 
    ExecuteAlerterTask<String> executeAlerterTask = new ExecuteAlerterTask<>(); 
    List<String> result = executeAlerterTask.process("TaskName", callable); 
    result.forEach(System.out::println); 
} 

或者,您可以設置帕拉姆在構造函數中,而不是的二傳手。