Java 让一个线程从共享列表中获取多个对象
我有一份按职业分类的可用员工列表(例如“程序员”、“测试人员”), 每个可用职业的数量存储在信号量中。 为了完成某项任务(每项任务都以不同的方式完成),如果给出职业列表(例如2个“程序员”,1个“经理”) 任务应该以“要么全有,要么全无”的方式完成——如果所有任务都可用,你就选择列表中的所有任务,否则等待所有人都可用 我通过使用BlockingQueue、信号量或手动锁定来限制对列表本身的访问,从而实现了这一点 我要问的是什么是正确的方法,如果可能的话,如何使release方法对其他线程仍然可用。您需要一个监视器()来完成您的任务。Java 让一个线程从共享列表中获取多个对象,java,concurrency,Java,Concurrency,我有一份按职业分类的可用员工列表(例如“程序员”、“测试人员”), 每个可用职业的数量存储在信号量中。 为了完成某项任务(每项任务都以不同的方式完成),如果给出职业列表(例如2个“程序员”,1个“经理”) 任务应该以“要么全有,要么全无”的方式完成——如果所有任务都可用,你就选择列表中的所有任务,否则等待所有人都可用 我通过使用BlockingQueue、信号量或手动锁定来限制对列表本身的访问,从而实现了这一点 我要问的是什么是正确的方法,如果可能的话,如何使release方法对其他线程仍然可用
它可以通过一个java.util.concurrent.Lock(ReentrantLock)和许多条件来实现。你的问题确实引起了我的兴趣。相当有趣的项目。下面是一个基本的实现,它似乎适合您的描述。请参见底部的可运行示例。它是相当有限的(不支持消极的获取,没有超时选项等等),但是它刚好可以使用它,并且您可以根据需要轻松地扩展它
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) + "}";
}
}
import java.util.*;
导入java.util.Map.Entry;
导入java.util.concurrent.Semaphore;
/**表示由不同字符串标识的一组信号量
*支持基本的获取和发布操作。如有必要,可添加其他操作
*@作者MPatashnik
*/
公共类信号组{
/**在建造时,可用于该项目的许可证总数*/
私人最终哈希图许可证;
/**此组中的信号量(按其标识符)*/
私有最终HashMap信号量;
/**监视此信号量组中操作使用情况的信号量*/
专用最终信号量操作锁;
/**线程映射到它们当前拥有的许可证*/
私有最终HashMap线程;
/**设置为true以查看线程获取和释放的打印输出*/
私有静态最终布尔调试=false;
/**创建信号量组。所有信号量都初始化为不公平。
*@param许可证-每个标识符字符串的许可证数
*/
公共信号组(Map许可证){
this.permissions=新的HashMap(permissions);
操作锁=新信号量(1);
信号量=新的HashMap();
threads=newhashmap();
对于(字符串s:permissions.keySet()){
信号量put(s,新信号量permissions.get(s));
}
}
/**试图获得给定的许可证
*@param许可证-获取许可证
*@throws InterruptedException-请参阅Semaphore.acquire()
*@throws-IllegalArgumentException-如果其中一个许可证允许,则此
*acquire是无法识别的字符串,或
*许可证获取计数为负数
*/
公共许可证(地图许可证)
抛出InterruptedException、IllegalArgumentException{
试一试{
操作锁。获取();
if(DEBUG)System.out.println(“已获取”+Thread.currentThread().getName());
对于(Map.Entry e:permissions.entrySet()){
信号量s=semaphores.get(e.getKey());
如果(s==null){
抛出新的IllegalArgumentException(“非法许可证名称”+e.getKey()+“不在”+this中”);
}
如果(如getValue()<0)
抛出新的IllegalArgumentException(“非法许可值”+e.getValue()+“必须为正”);
如果(s.availablePermits()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();
}
}
}
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;
}
}
}