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);
}
public Future 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