Javaセマフォの例
Javaでは、Semaphore
を使用して、特定のリソースにアクセスするスレッドの数を制限できます。
1. セマフォとは何ですか?
つまり、セマフォは一連の許可(チケット)を維持し、各acquire()
はセマフォから許可(チケット)を取得し、各release()
は許可(チケット)をセマフォに戻します。 許可証(チケット)が利用できない場合、acquire()
は利用可能になるまでブロックされます。
// 5 tickets Semaphore semaphore = new Semaphore(5); // take 1 ticket semaphore.acquire(); // 4 semaphore.availablePermits(); // return back ticket semaphore.release(); // 5 semaphore.availablePermits();
2. Javaセマフォ
ExecutorService
で実行されるタスクの数を制限するJavaセマフォの例。 この例では、5つのCallable
タスクがExecutorService
に送信されますが、同時に実行されているのは2つのタスクのみです。
TaskLimitSemaphore.java
package com.example.concurrency.synchronizer.semaphore; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.*; // Throttle task submission public class TaskLimitSemaphore { private final ExecutorService executor; private final Semaphore semaphore; public TaskLimitSemaphore(ExecutorService executor, int limit) { this.executor = executor; this.semaphore = new Semaphore(limit); } publicFuture submit(final Callable task) throws InterruptedException { semaphore.acquire(); System.out.println("semaphore.acquire()..."); return executor.submit(() -> { try { return task.call(); } finally { semaphore.release(); System.out.println("semaphore.release()..."); } }); } private static final DateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newCachedThreadPool(); // Only 2 tasks are able to run concurrently TaskLimitSemaphore obj = new TaskLimitSemaphore(executor, 2); obj.submit(() -> { System.out.println(getCurrentDateTime() + " : task1 is running!"); Thread.sleep(2000); System.out.println(getCurrentDateTime() + " : task1 is done!"); return 1; }); obj.submit(() -> { System.out.println(getCurrentDateTime() + " : task2 is running!"); Thread.sleep(2000); System.out.println(getCurrentDateTime() + " task2 is done!"); return 2; }); obj.submit(() -> { System.out.println(getCurrentDateTime() + " task3 is running!"); Thread.sleep(2000); System.out.println(getCurrentDateTime() + " task3 is done!"); return 3; }); obj.submit(() -> { System.out.println(getCurrentDateTime() + " task4 is running!"); Thread.sleep(2000); System.out.println(getCurrentDateTime() + " task4 is done!"); return 4; }); obj.submit(() -> { System.out.println(getCurrentDateTime() + " task5 is running!"); Thread.sleep(2000); System.out.println(getCurrentDateTime() + " task5 is done!"); return 5; }); executor.shutdown(); } private static String getCurrentDateTime() { return sdf.format(new Date()); } }
出力
semaphore.acquire()... semaphore.acquire()... 2018/12/06 18:45:22 : task1 is running! 2018/12/06 18:45:22 : task2 is running! 2018/12/06 18:45:24 : task1 is done! 2018/12/06 18:45:24 task2 is done! semaphore.release()... semaphore.acquire()... semaphore.release()... semaphore.acquire()... 2018/12/06 18:45:24 task3 is running! 2018/12/06 18:45:24 task4 is running! 2018/12/06 18:45:26 task4 is done! 2018/12/06 18:45:26 task3 is done! semaphore.acquire()... semaphore.release()... semaphore.release()... 2018/12/06 18:45:26 task5 is running! 2018/12/06 18:45:28 task5 is done! semaphore.release()...
3. ミューテックス
つまり、常にnew Semaphore(1)
であり、特定のリソースへのアクセスが許可されるスレッドは1つだけです。
PrintSequenceCallable.java
package com.example.concurrency.synchronizer.semaphore; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class SshLoginSemaphore { private final Semaphore mutex; // only 1 user is allow public SshLoginSemaphore() { this.mutex = new Semaphore(1); } private void ssh(String user) throws InterruptedException { mutex.acquire(); System.out.println(getCurrentDateTime() + " : " + user + " mutex.acquire()"); Thread.sleep(2000); mutex.release(); System.out.println(getCurrentDateTime() + " : " + user + " mutex.release()"); } private static final DateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5); SshLoginSemaphore task = new SshLoginSemaphore(); // submit 3 tasks executor.submit(() -> { try { task.ssh("example"); } catch (InterruptedException e) { e.printStackTrace(); } }); executor.submit(() -> { try { task.ssh("yflow"); } catch (InterruptedException e) { e.printStackTrace(); } }); executor.submit(() -> { try { task.ssh("zilap"); } catch (InterruptedException e) { e.printStackTrace(); } }); executor.shutdown(); } private static String getCurrentDateTime() { return sdf.format(new Date()); } }
出力
時間(秒)を確認します。1つのスレッドのみが許可され、1つのacquire()
と1つのrelease()
が許可されます
2018/12/06 18:54:25 : example mutex.acquire() 2018/12/06 18:54:27 : yflow mutex.acquire() 2018/12/06 18:54:27 : example mutex.release() 2018/12/06 18:54:29 : zilap mutex.acquire() 2018/12/06 18:54:29 : yflow mutex.release() 2018/12/06 18:54:31 : zilap mutex.release()
ソースコードをダウンロード
$ git clone https://github.com/example/java-concurrency.git