2016-07-24 27 views
4

我正在尋找一種方法來限制可以在Java中使用信號量或類似方法運行某段代碼的線程數量。限制在Java中運行某段代碼的線程數的最佳方式?

我們正在研究類似於Google Guava RateLimiter的東西 - 但不是限制每秒鐘的調用次數,而是限制運行關鍵代碼段的線程數。

之所以需要這個,是因爲我們使用的某些庫在這裏有問題,所以我們只是尋找一個快速的解決方法。

+1

爲什麼不能使用java.util.concurrent.Semaphore? –

回答

4

這正是java.util.concurrent.Semaphore設計做。您創建一個Semaphore像這樣:

final int MAX_NOF_THREADS = 5; 
final Semaphore mySemaphore = new Semaphore(MAX_NOF_THREADS); 

那麼對於關鍵領域,你會怎麼做:

try { 
    mySemaphore.aquire(); // This will hang until there is a vacancy 
    do_my_critical_stuff(); 
} finally { 
    mySemaphore.release(); 
} 

...就這麼簡單。

+1

我完全同意'Semaphore's,他們應該是這項任務的首選。不過,有一點值得一提:從技術上講,允許一個線程進行多次獲取。這意味着一些邏輯錯誤(例如錯誤的遞歸)可能導致線程利用率不足甚至導致死鎖。但如果使用得當,它們就是這裏的正確工具。 –

4

雖然,Semaphore是最好的選擇(請看@Bex的答案)如果你小心,也可以使用ExecutorService。只是包裝的一段代碼,你想從無限的併發訪問,以保護爲Callable任務,並提交這樣的任務執行人服務:

// Task that will be executed 
public class MyTask implements Callable<Void> { 
    @Override 
    public Void call() { 
     // Do the work here 
     return null; 
    } 
} 

// Service to execute tasks in no more than 5 parallel threads 
// Cache it after creation and use when you need to execute a task 
int maxThreadsCount = 5; 
ExecutorService executor = Executors.newFixedThreadPool(maxThreadsCount); 

// Execute a task. It will wait if all 5 threads are busy right now. 
executor.submit(new MyTask()); 

隨着ExecutorService你也可以使用Runnable代替CallableinvokeAll()代替execute,等待任務完成,取消任務,返回值並做一些其他有用的事情。

的Java 8使它更簡單,你可以使用lambda表達式而不是定義任務類:

executor.submit(() -> { 
    // Do the work here 
}); 
+1

需要記住的一點是,Executor工廠將構建由無界作業隊列支持的ExecutorService實現。根據頻率和運行作業的時間長短,這可能會對系統產生負面影響。在某些情況下,由於在內存中排隊的作業數量有限,它可能會降低應用程序的性能。在這些情況下,可能值得創建自己的** ThreadPoolExecutor **,其中包含一個有界的工作隊列和一個自定義的** RejectedExecutionHandler **。看到這段代碼如何是關鍵的,你可能需要無限制的版本。請注意影響。 – Jeremiah

相關問題