在Java中,我们可以使用Semaphore
来限制访问特定资源的线程数。
1.什么是信号量?
简而言之,信号量维护一组许可(票证),每个acquire()
)将从信号量中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信号量
一个Java Semaphore示例,用于限制ExecutorService
运行的任务数量。 在此示例中,有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)
,仅允许一个线程访问特定资源。
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());
}
}
输出量
查看时间(秒),只允许一个线程,一个acquire()
和一个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