Javaセマフォの例
Javaでは、 `Semaphore`を使って特定のリソースにアクセスするスレッドの数を制限することができます。
1.セマフォとは何ですか?
要するに、セマフォーは許可(チケット)のセットを維持し、各 `acquire()`はセマフォーから許可(チケット)を取り、それぞれの `release()`は許可(チケット)をセマフォーに返送します。 permit(チケット)が利用できない場合、 `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 Semaphoreの例です。この例では、5つの `Callable`タスクが
ExecutorService`に提出されますが、同時に実行されるタスクは2つだけです。
TaskLimitSemaphore.java
package com.mkyong.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 <T> Future<T> submit(final Callable<T> 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.mkyong.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("mkyong");
} 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つのスレッドのみ、 `acquire()`と1つの `release()`を確認してください。
2018/12/06 18:54:25 : mkyong mutex.acquire() 2018/12/06 18:54:27 : yflow mutex.acquire() 2018/12/06 18:54:27 : mkyong 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/mkyong/java-concurrency.git
参考文献
-
ウィキペディア –
セマフォ(プログラミング)]。
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html
[Oracle
doc – セマフォ]