我有一份按职业分类的可用员工名单(例如“程序员”,“测试员”), 每个可用职业的数量都存储在信号量中。 完成某项任务 - 每项任务都在不同的线程中完成 - 如果给出了专业列表(例如2“程序员”,1“经理”) 并且任务应该以全有或全无的方式获取它们 - 如果一切都可用,则取出列表中的所有内容,否则等待每个人都可用。
我已经通过使用BlockingQueue,Semaphore限制列表本身的访问权限或者只是手动锁定它来完成此操作。
我要问的是这样做的正确方法是什么,如果可能的话,如何让其他线程可以使用release方法。
答案 0 :(得分:1)
您需要一台显示器(http://en.wikipedia.org/wiki/Monitor_%28synchronization%29)来完成您的任务。 它可以通过java.util.concurrent.Lock(ReentrantLock)和锁上的许多条件来实现。
答案 1 :(得分:1)
你的问题确实引起了我的兴趣。相当有趣的项目。这是一个基本的实现,似乎适用于您的描述。请参阅底部以获取可运行的示例。它相当有限(不支持负面获取,没有超时选项等),但它已经足够使用它,你可以根据需要轻松扩展它。
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Semaphore;
/** Represents a group of semaphores identified by distinct strings
* Supports basic acquire and release operations. Other operations could be added as necessary
* @author MPatashnik
*/
public class SemaphoreGroup {
/** The total number of permits available to this, as it was constructed */
private final HashMap<String, Integer> permits;
/** The semaphores in this group, by their identifier */
private final HashMap<String, Semaphore> semaphores;
/** The semaphore monitoring use of operations in this SemaphoreGroup */
private final Semaphore operationLock;
/** A map of threads to permits they currently own */
private final HashMap<Thread, Map<String, Integer>> threads;
/** Set to true to see printing output of threads acquiring and releasing */
private static final boolean DEBUG = false;
/** Creates a SemaphoreGroup. All semaphores are initialized as unfair.
* @param permits - the Number of permits for each identifier string
*/
public SemaphoreGroup(Map<String, Integer> permits) {
this.permits = new HashMap<String, Integer>(permits);
operationLock = new Semaphore(1);
semaphores = new HashMap<String, Semaphore>();
threads = new HashMap<Thread, Map<String, Integer>>();
for(String s : permits.keySet()){
semaphores.put(s, new Semaphore(permits.get(s)));
}
}
/** Attempts to acquire the given permits
* @param permits - the permits to acquire
* @throws InterruptedException - see Semaphore.acquire()
* @throws IllegalArgumentException - If one of the permits this wants to
* acquire is an unrecognized string, or any of the
* permit acquisition counts is negative
*/
public void acquire(Map<String, Integer> permits)
throws InterruptedException, IllegalArgumentException{
try{
operationLock.acquire();
if(DEBUG) System.out.println("Acquired " + Thread.currentThread().getName());
for(Map.Entry<String, Integer> e : permits.entrySet()){
Semaphore s = semaphores.get(e.getKey());
if(s == null){
throw new IllegalArgumentException("Illegal Permit Name " + e.getKey() + " Not in " + this);
}
if(e.getValue() < 0)
throw new IllegalArgumentException("Illegal Permit Value " + e.getValue() + " Must be positive");
if(s.availablePermits() < e.getValue()){
operationLock.release();
if(DEBUG) System.out.println("Released " + Thread.currentThread().getName());
//Not enough permits - wait on semaphore until someone releases, then try again
synchronized(operationLock){
operationLock.wait();
}
acquire(permits);
return;
}
}
//All semaphores ok. Do acquiring and exit
for(Map.Entry<String, Integer> e : permits.entrySet()){
semaphores.get(e.getKey()).acquire(e.getValue());
}
Thread t = Thread.currentThread();
//Update information of this thread owning permits
Map<String, Integer> currentlyOwned = threads.get(t);
if(currentlyOwned == null){
threads.put(t, new HashMap<String, Integer>(permits));
}
else{
HashMap<String, Integer> totalOwned = new HashMap<String, Integer>(permits);
for(Map.Entry<String, Integer> e : permits.entrySet()){
totalOwned.put(e.getKey(),
e.getValue()
+ (totalOwned.get(e.getKey()) == null ? 0 : currentlyOwned.get(e.getKey())));
}
threads.put(t, totalOwned);
}
}
finally{
operationLock.release();
if(DEBUG) System.out.println("Released " + Thread.currentThread().getName());
}
}
/** Attempts to release the given amounts of the given permits.
* Won't release more permits for any identifier than this currently owns.
* @param permits - the permits to release.
* @throws InterruptedException - see Semaphore.acquire
*/
public void release(Map<String, Integer> permits) throws InterruptedException{
try{
operationLock.acquire();
if(DEBUG) System.out.println("Acquired " + Thread.currentThread().getName());
Thread t = Thread.currentThread();
//Check to see if this thread has any permits at all
if(! threads.containsKey(t))
return;
for(Map.Entry<String, Integer> e : permits.entrySet()){
Semaphore s = semaphores.get(e.getKey());
if(s == null){
throw new IllegalArgumentException("Illegal Permit Name " + e.getKey() + " Not in " + this);
}
int has = threads.get(t).containsKey(e.getKey()) ? threads.get(t).get(e.getKey()) : 0;
int toRemove = Math.min(e.getValue(), has);
s.release(toRemove);
threads.get(t).put(e.getKey(), has - toRemove);
}
if(DEBUG){
System.out.println("\nReleasing " + t);
System.out.println(threads.toString().replaceAll("},", "}\n"));
}
//Ok, notify a thread wanting to acquire
synchronized(operationLock){
operationLock.notify();
}
}finally{
operationLock.release();
if(DEBUG) System.out.println("Released " + Thread.currentThread().getName());
}
}
/** Releases all permits this currently owns for all identifiers within this Semaphore Group
* @throws InterruptedException - see Semaphore.acquire
*/
public void releaseAll() throws InterruptedException{
try{
operationLock.acquire();
if(DEBUG) System.out.println("Acquired " + Thread.currentThread().getName());
Thread t = Thread.currentThread();
if(! threads.containsKey(t)) return;
HashMap<String, Integer> permits = new HashMap<String, Integer>(threads.get(t));
operationLock.release();
if(DEBUG) System.out.println("Released " + Thread.currentThread().getName());
release(permits);
}finally{
operationLock.release();
if(DEBUG) System.out.println("Released " + Thread.currentThread().getName());
}
}
/** Returns the permits (by identifier) this SemaphoreGroup still has available. */
public Map<String, Integer> getAvailablePermits(){
HashMap<String, Integer> available = new HashMap<>();
for(Entry<String, Semaphore> e : semaphores.entrySet()){
available.put(e.getKey(), e.getValue().availablePermits());
}
return available;
}
/** Returns the set of valid identifying strings for this semaphore group */
public Set<String> getIdentifyingStrings(){
return semaphores.keySet();
}
/** Returns the available permits out of the total as the toString */
@Override
public String toString(){
Map<String, Integer> available = getAvailablePermits();
String s = "{";
for(Entry<String, Integer> e : permits.entrySet()){
s += e.getKey() + "=" + available.get(e.getKey()) + "/" + e.getValue() + ", ";
}
return s.substring(0, s.length() - 2) + "}";
}
}
Runnable随播:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.HashMap;
import java.util.LinkedList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ThreadRunner extends JFrame {
private static LinkedList<Worker> threads;
private static SemaphoreGroup semaphore;
private static HashMap<String, Integer> totalPermits;
public ThreadRunner(){
setLayout(new BorderLayout());
add(new InfoPanel(), BorderLayout.CENTER);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
repaint();
setVisible(true);
}
static class InfoPanel extends JPanel{
public InfoPanel(){
setPreferredSize(new Dimension(600, 500));
}
@Override
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setFont(new Font("Arial", Font.PLAIN, 15));
int x = 20;
int y = 20;
g2d.drawString("Available: " + semaphore.toString(), x, y);
y += 50;
for(Worker t : threads){
if(t.working) g2d.setColor(Color.RED);
else g2d.setColor(Color.BLACK);
g2d.drawString(t.getName() + "-" + t.status + " : " + t.job.toString(), x, y);
y += 25;
if(! t.working) g2d.drawString("Next: " + t.nextJob.toString(), x + 150, y);
y += 35;
}
}
}
static class Worker extends Thread{
private volatile String status;
private boolean working;
private HashMap<String, Integer> job = new HashMap<>();
private HashMap<String, Integer> nextJob = new HashMap<>();
private int jobIndex;
private static final int WORK_TIME = 2000;
public Worker(int i){
super("Worker " + i);
jobIndex = 1;
}
@Override
public void run(){
try{
createNextJob();
while(true){
createNextJob();
HashMap<String, Integer> aJob = nextJob;
semaphore.acquire(aJob);
job = aJob;
working = true;
for(int i = 0; i < 10; i++){
Thread.sleep(WORK_TIME / 10);
status = ((i + 1) * 10) + "% done of Job " + jobIndex;
}
semaphore.releaseAll();
working = false;
job.clear();
jobIndex++;
}
} catch (InterruptedException e) {}
}
private void createNextJob(){
nextJob = new HashMap<>();
nextJob.put("Bronze", (int)(totalPermits.get("Bronze") * Math.random()));
nextJob.put("Silver", (int)(totalPermits.get("Silver") * Math.pow(Math.random(), 2)));
nextJob.put("Gold", (int)(totalPermits.get("Gold") * Math.pow(Math.random(), 3)));
nextJob.put("Platinum", (int)(totalPermits.get("Platinum") * Math.pow(Math.random(), 4)));
}
@Override
public String toString(){
return getName();
}
}
public static void main(String[] args){
totalPermits = new HashMap<>();
totalPermits.put("Bronze", 15);
totalPermits.put("Silver", 10);
totalPermits.put("Gold", 5);
totalPermits.put("Platinum", 2);
semaphore = new SemaphoreGroup(totalPermits);
threads = new LinkedList<Worker>();
final int NUMB_WORKERS = 5;
for(int i = 0; i < NUMB_WORKERS; i++){
threads.add(new Worker(i));
}
ThreadRunner tr = new ThreadRunner();
//Start worker threads
for(Worker w : threads){
w.start();
}
//Monitor gui in main thread
while(true){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
tr.repaint();
}
}
}
答案 2 :(得分:1)
这是一个完整的,有效的,人为的例子,我相信它符合概述的要求。
它跟踪信号量中可用资源的总数,BlockingQueues中的实际资源以及BlockingQueues中的任务。
如果它无法立即获取任务所需的资源,它会将任务重新提交到队列的后面(这可以通过其他方式完成,但在此示例中,它使用的是有界的工作线程池,因此您不会'我们一定希望他们等到资源可用,因为这可能会阻止可能立即运行的其他任务的并行化。)
package so.thread.resources;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class MultiResourcesMain {
public static int numManagers = 5;
public static int numProgrammers = 15;
public static int numTesters = 5;
public static Semaphore managersLease = new Semaphore(numManagers);
public static Semaphore programmersLease = new Semaphore(numProgrammers);
public static Semaphore testersLease = new Semaphore(numTesters);
public static BlockingQueue<Manager> managers = new LinkedBlockingQueue<Manager>();
public static BlockingQueue<Programmer> programmers = new LinkedBlockingQueue<Programmer>();
public static BlockingQueue<Tester> testers = new LinkedBlockingQueue<Tester>();
public static Random rand = new Random();
public static BlockingQueue<Task> tasks = new LinkedBlockingQueue<>();
public static Object resourceLock = new Object();
public static AtomicBoolean running = new AtomicBoolean(true);
public static AtomicInteger tasksRun = new AtomicInteger(0);
public static AtomicInteger resubmits = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
// prime the resources
for (int i = 0; i < numManagers; i++) {
managers.add(new Manager());
}
for (int i = 0; i < numProgrammers; i++) {
programmers.add(new Programmer());
}
for (int i = 0; i < numTesters; i++) {
testers.add(new Tester());
}
int numTasks = 100;
int managersRandLimit = numManagers + 1;
int programmersRandLimit = numProgrammers + 1;
int testersRandLimit = numTesters + 1;
// generate tasks to execute with random resource requirements
for (int i = 0; i < numTasks; i++) {
tasks.add(new Task(UUID.randomUUID().toString(), new TaskResources(rand.nextInt(managersRandLimit), rand.nextInt(programmersRandLimit), rand.nextInt(testersRandLimit))));
}
// spin up worker threads
int numWorkers = 10;
ExecutorService taskExecutor = Executors.newFixedThreadPool(numWorkers);
for (int i = 0; i < numWorkers; i++) {
taskExecutor.submit(new Worker());
}
while (tasksRun.get() < numTasks) {
Thread.sleep(10);
}
running.set(false);
taskExecutor.shutdown();
taskExecutor.awaitTermination(2, TimeUnit.SECONDS);
System.out.println(String.format("Done, ran %d tasks and resubmitted %d tasks due to insufficient resources at acquire time", tasksRun.get(), resubmits.get()));
}
public static class Worker implements Runnable {
@Override
public void run() {
while (running.get()) {
try {
Task task = tasks.poll(1, TimeUnit.SECONDS);
if (null != task) {
if (acquireResources(task.resources)) {
runTask(task);
releaseResources(task.resources);
} else {
// couldn't execute task now, returning to task queue
System.out.println(String.format("[%s :: %s] !!! Couldn't acquire resources for Task %s, resubmitting",
Thread.currentThread().getName(), new Date(), task.id));
tasks.add(task);
resubmits.getAndIncrement();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(String.format("[%s :: %s] >>> Thread shutdown",
Thread.currentThread().getName(), new Date()));
}
}
public static void runTask(Task task) {
Date now = new Date();
long elapsed = now.getTime() - task.created.getTime();
System.out.println(String.format("[%s :: %s] *** Running task with %d managers, %d programmers & %d testers, waited %d millis to execute for id %s",
Thread.currentThread().getName(), now, task.resources.managers, task.resources.programmers, task.resources.testers, elapsed, task.id));
tasksRun.getAndIncrement();
}
public static void releaseResources(TaskResources res) {
synchronized (resourceLock) {
managersLease.release(res.managers);
programmersLease.release(res.programmers);
testersLease.release(res.testers);
}
}
public static boolean acquireResources(TaskResources res) {
synchronized (resourceLock) {
boolean acquiredManagers = false;
boolean acquiredProgrammers = false;
boolean acquiredTesters = false;
acquiredManagers = managersLease.tryAcquire(res.managers);
if (acquiredManagers) {
acquiredProgrammers = programmersLease.tryAcquire(res.programmers);
if (acquiredProgrammers) {
acquiredTesters = testersLease.tryAcquire(res.testers);
}
}
if (acquiredManagers && acquiredProgrammers && acquiredTesters) {
return true;
} else {
// return unused resources
if (acquiredProgrammers) {
programmersLease.release(res.programmers);
}
if (acquiredManagers) {
managersLease.release(res.managers);
}
return false;
}
}
}
public abstract static class Person {
}
public static class Manager extends Person {
}
public static class Programmer extends Person {
}
public static class Tester extends Person {
}
public static class Task {
public String id;
public TaskResources resources;
public Date created = new Date();
public Task(String id, TaskResources resources) {
this.id = id;
this.resources = resources;
}
}
public static class TaskResources {
public int managers;
public int programmers;
public int testers;
public TaskResources(int managers, int programmers, int testers) {
this.managers = managers;
this.programmers = programmers;
this.testers = testers;
}
}
}