I am testing the ThreadPoolExecutor. I print out some debug info in the customized ThreadPoolExecutor's execute() method. The execute() method is called by an instance of the customized ThreadPoolExecutor. When I look at the print out, I found there are some duplicated print out which is from the main thread. I am wondering why the main thread calls the customized ThreadPoolExecutor's execute() method? The runnable code is attached here:
----------Thead: main/1
[PoolSize/CorePoolSize] [1/2]
----------Thead: pool-1-thread-1/11
[PoolSize/CorePoolSize] [1/2]
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyThreadPoolExecutorTest2
{
private List<MyRunnable> myRunnables = new ArrayList<>();
public static void main(String[] args)
{
new MyThreadPoolExecutorTest2().test();
}
public void test()
{
int poolSize = 2;
int maxPoolSize = 6;
int threadPoolKeepAliveTimeInSec = 30;
ExecutorService threadPoolExecutor =
new MySimpleThreadPoolExecutor2(poolSize, maxPoolSize, threadPoolKeepAliveTimeInSec);
int numOfThread = 5;
System.out.println("Start thread pool test with corePoolSize=" + poolSize + ", maxPoolSize=" + maxPoolSize
+ ", actualThreads=" + numOfThread);
for (int i = 0; i < numOfThread; i++)
{
MyRunnable tempRunnable = new MyRunnable(i + 1, "PoolTest" + (i + 1));
myRunnables.add(tempRunnable);
threadPoolExecutor.execute(tempRunnable);
}
System.out.println("********* wait for a while");
try
{
Thread.sleep(20000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("^^^^^^^^^^ shutdown them all");
for (MyRunnable runnable : myRunnables)
{
runnable.shutdown();
}
System.out.println("Ended thread pool test.");
System.exit(0);
}
public class MyRunnable implements Runnable
{
private int id = 0;
private String name = "";
private boolean shutdown = false;
public MyRunnable(int id, String name)
{
this.id = id;
this.name = name;
}
#Override
public void run()
{
System.out.println("++++ Starting Thread: " + id + ":" + name);
while (!shutdown)
{
try
{
Thread.sleep(200);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.println("---- Ended Thread: " + id + ":" + name);
}
public void shutdown()
{
shutdown = true;
}
}
}
class MySimpleThreadPoolExecutor2 extends ThreadPoolExecutor
{
private static int peakActiveThreads = 0;
private String taskInfo = "";
public MySimpleThreadPoolExecutor2(int nThreads, int maxThreads, int threadPoolKeepAliveTimeInSec)
{
super(nThreads, maxThreads, threadPoolKeepAliveTimeInSec * 1000L, TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>());
System.out.println("MySimpleThreadPoolExecutor::MySimpleThreadPoolExecutor(), threadPoolSize=" + nThreads
+ ", maxThreadCount=" + maxThreads + ", threadPoolKeepAliveTimeInSec=" + threadPoolKeepAliveTimeInSec);
}
#Override
public void beforeExecute(Thread t, Runnable r)
{
int activeCount = getActiveCount();
if (MySimpleThreadPoolExecutor2.peakActiveThreads < activeCount)
{
MySimpleThreadPoolExecutor2.peakActiveThreads = activeCount;
}
taskInfo = r.toString();
String msg =
"BeforeE thread(name:id)::" + t.getName() + ":" + t.getId() + ", task::" + r.toString() + "\n"
+ threadPoolInfoStr();
System.out.println("ThreadInfo before, MySimpleThreadPoolExecutor::beforeExecute(), " + msg);
super.beforeExecute(t, r);
}
#Override
public void execute(Runnable command)
{
beforeExecute(Thread.currentThread(), command);
super.execute(command);
}
public String threadPoolInfoStr()
{
return String
.format("----------Thead: %s/%d\n[PoolSize/CorePoolSize] [%d/%d]\nActive: %d\nCompleted: %d\nTask: %d"
+ "\nisShutdown: %s\nisTerminated: %s\npeakActiveThreads: %d\nTaskInfo: %s\nQueueSize: %d----------",
Thread.currentThread().getName(), Thread.currentThread().getId(), getPoolSize(),
getCorePoolSize(), getActiveCount(), getCompletedTaskCount(), getTaskCount(), isShutdown(),
isTerminated(), MySimpleThreadPoolExecutor2.peakActiveThreads, taskInfo, getQueue().size());
}
}
Well, you explicitly call beforeExecute from the overridden execute() method in your subclass :
#Override
public void execute(Runnable command)
{
beforeExecute(Thread.currentThread(), command);
super.execute(command);
}
As the execute method is called from the client thread (in your case the main thread) it prints out an entry for the calling thread too. The super class however calls the beforeExecute method from its worker thread, prior to executing a submitted task.
Note the javadoc for beforeExecute :
Method invoked prior to executing the given Runnable in the given thread. This method is invoked by thread t that will execute task r, and may be used to re-initialize ThreadLocals, or to perform logging.
Your explicit invocation from execute() is an extra invocation from the calling thread.
I am wondering why the main thread calls the customized ThreadPoolExecutor's execute() method?
because it is explicitely called in the code:
public static void main(String[] args) {
new MyThreadPoolExecutorTest2().test();
}
public void test() {
...
threadPoolExecutor = new MySimpleThreadPoolExecutor2();
...
threadPoolExecutor.execute(tempRunnable);
^^^^^^^
...
}
Related
I’m working on the task of monitoring the execution of query, it became necessary to check the query after a certain time. Please, help me, how to implement the method poll correctly? Is it possible to do this in a separate thread? For example, I want to log every iteration of the loop and end the stream on the number 8. how to implement it correctly? Thanks!
public class MyTimerTask implements Runnable {
String name;
private boolean isActive;
void disable(){
isActive=false;
}
MyTimerTask(String name){
isActive = true;
this.name = name;
run();
}
#Override
public void run() {
System.out.println(name + " Start at :" + new Date());
try {
completeTask();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + " Finish at:" + new Date());
}
private void completeTask() throws InterruptedException {
for(int i = 0; i<10;i++){
System.out.println(i);
Thread.sleep(1000);
}
}
public static void main(String args[]){
new MyTimerTask("device");
}
}
Try something like this:
public class MyTimerTask implements Runnable {
#Override
public void run() {
System.out.println(name + " Start at :" + new Date());
completeTask();
System.out.println(name + " Finish at:" + new Date());
}
private void completeTask() throws InterruptedException {
for(int i = 0; i<10;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
new Thread(new MyTimerTask(), "device").start();
}
}
Do not need those fields, java.lang.Thread have those.
Do not call methods from the constructor that requires the instance to be fully created. EG: do not call run() from it.
InterruptedExceptions should be caught, but in this case you may want to swallow it, as it is not signalling an unfinished job...
To create a new thread use the Thread: You can specify the Runnable instance and/or name as arguments of the constructor. Or you can extend it and call super() with the name in the constructor, and implement run() in it.
.
public class MyTimerTask extends Thread {
public MyTimerTask() {
super("device");
}
#Override
public void run() {
System.out.println(name + " Start at :" + new Date());
completeTask();
System.out.println(name + " Finish at:" + new Date());
}
private void completeTask() throws InterruptedException {
for(int i = 0; i<10;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
new Thread().start();
}
}
You implementation works fine.
You just need include on main method:
public static void main(String args[]){
new Thread(new MyTimerTask("device")).start();
}
Have in mind that according this implementation you'll run the function only 10 times.
As you have a status flag maybe you can use it changing the loop intructoin.
while (isActive) {
System.out.println(name + " Start at :" + Instant.now());
I have a need of a threadpool executor, which needs to complete an exact number (same) tasks.
It has to be able to re-submit failed tasks for an n number of times. If any of the tasks fail for more than n, then the threadpool should shutdown and not continue to process any other tasks.
I have tried to combine 2 approaches which I've found in different answers - one for re-submitting failed tasks by overriding ThreadPoolExecutor.afterExecute, and subclassing CountDownLatch so that threads waiting on the latch get interrupted and the executor shuts down.
So far, this is the subclassed countdown latch:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class AbortableCountDownLatch extends CountDownLatch {
protected boolean aborted = false;
public AbortableCountDownLatch(int count) {
super(count);
}
/**
* Unblocks all threads waiting on this latch and cause them to receive an
* AbortedException. If the latch has already counted all the way down,
* this method does nothing.
*/
public void abort() {
if( getCount() == 0 )
return;
this.aborted = true;
while(getCount() > 0)
countDown();
}
#Override
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
final boolean rtrn = super.await(timeout,unit);
if (aborted)
throw new AbortedException();
return rtrn;
}
#Override
public void await() throws InterruptedException {
super.await();
if (aborted)
throw new AbortedException();
}
public static class AbortedException extends InterruptedException {
public AbortedException() {
}
public AbortedException(String detailMessage) {
super(detailMessage);
}
}
}
And the thread pool executor:
public class MyThreadPoolExecutor extends ThreadPoolExecutor {
private static final int RETRY_LIMIT = 3;
private Map<Runnable, Integer> retriedTasks = new ConcurrentHashMap<>();
private AbortableCountDownLatch latch;
public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, AbortableCountDownLatch latch) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
this.latch = latch;
}
#Override
public void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
// If submit() method is called instead of execute()
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException e) {
t = e;
} catch (ExecutionException e) {
t = e.getCause();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (t != null) {
retriedTasks.put(r, retriedTasks.getOrDefault(r, 0) + 1);
System.out.println("Retries for " + r + " -> " + retriedTasks.get(r));
/* check to see if we have retried this task too many times, if so - shutdown */
if (retriedTasks.containsKey(r) && retriedTasks.get(r) > RETRY_LIMIT) {
System.err.println("Thread failed for more than " + RETRY_LIMIT + " times, aborting everything..");
this.latch.abort();
} else {
System.err.println("Thread threw exception " + t.getMessage() + ". Retry-ing task...");
execute(r);
}
} else {
/* clear any previous retry count for this runnable */
retriedTasks.remove(r);
}
}
}
And a main would be using them like this:
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MainProcessor {
public static void main(String[] args) {
AbortableCountDownLatch latch = new AbortableCountDownLatch(5);
ThreadPoolExecutor threadPoolExecutor = new MyThreadPoolExecutor(8, 8, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), latch);
for (int i = 0; i < 5; i++) {
threadPoolExecutor.submit(() -> {
System.out.println("Started thread " + Thread.currentThread().getName());
Random random = new Random();
try {
Thread.sleep(random.nextInt(7000));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (random.nextBoolean()){
System.err.println("Thread " + Thread.currentThread().getName() + " failed - throwing exception..");
throw new RuntimeException("Thread " + Thread.currentThread().getName() + "failed! spectacularly :!");
}
else {
System.out.println("Thread " + Thread.currentThread().getName() + " finished.");
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
threadPoolExecutor.shutdownNow();
}
threadPoolExecutor.shutdown();
}
}
Does this approach look correct? I don't particularly like that the latch has to be passed to both the thread pool executor and to the actual Runnable. Is there a standard way of achieving this? I am fine with a Scala version too.
I have seen others who suggest that the tasks should re-submit itself to the pool in case of failure, but that doesn't seem a good idea, as the task should only be responsible of the actual running logic, and not execution details.
You could use a Task-Wrapper that does the work, then it would be rather simple:
public class TaskWrapper implements Runnable
{
private Runnable task;
private int maxResubmits;
private ThreadPoolExecutor executor;
private CountDownLatch latch;
public TaskWrapper(Runnable task, int maxResubmits, ThreadPoolExecutor executor, CountDownLatch latch) {
this.task=task;
this.maxResubmits=maxResubmits;
this.executor=executor;
this.latch=latch;
executor.submit(this);
}
public void run() {
try {
task.run();
latch.countdoun();
}
catch(Exception e) {
maxResubmits--;
if(maxResubmits>0)
executor.submit(this);
else
{
latch.countdoun();
executor.shutdownNow()
}
}
}
}
You now only need to create the latch, call your tasks and then wait for the execution:
List<Runnable> tasks;
int maxResubmits;
CountDownLatch latch=new CountDownLatch(tasks.size());
tasks.forEach(task->new TaskWrapper(task,maxResubmits,executor,latch));
latch.await();
if(!executor.isShutdown())
executor.shutdown();
I am generating a child thread when I receive data from user.
What are the steps if I want to dispose the previous user child thread if the same user sends data again and wants to generate a new user child thread again?
Right, so java can't dispose of the thread, a thread simply runs until it terminates.
So:
To get rid of the thread you need to allow the threads run method to end and then get rid of all references to the Thread and any Runnable it's constructed with.
You want to toggle the thread finishing so, a simple example:
class SimpleRunnable implements Runnable {
public volatile boolean run = true; //Volatile for thread safety.
public void run() {
while(run) {
System.out.println("WHOOOO!"); //Boy, will this be annoying
}
}
}
Creating a thread from this runnable:
SimpleRunnable run = new SimpleRunnable();
Thread thread = new Thread(run);
Thread.start(); //run thread
//Stop thread
run.run=false;
//Thread will be removed when out of scope
Youu need to create a Runnable per user in your case, and then call set the stop variable when a new thread is created.
For example, you could store each runnable in a ConcurrentHashMap by userId.
ConcurrentHashMap<String,SimpleRunnable> runnablesByUser = new ConcurrentHashMap<>();
public void startNewThreadForUser(String userId){
//Time passes, retrieve and kill old thread:
SimpleRunnable oldRunnable = runnableByUser.get(userId);
if(oldRunnable!=null){
oldRunnable.run=false;
}
SimpleRunnable newRunnableUserOne = new SimpleRunnable();
runnablesByUser.put(userId,newRunnableUserOne);
Thread thread = new Thread(newRunnableUserOne);
thread.start();
}
Calls to the method would then kill an old thread if found, release the old one from scope by replacing it with a new one in the ConcurrentHashMap and finally start the new thread.
Like so:
public void startThreeThreads(){
startNewThreadForUser("User1");//starts Thread for User1
startNewThreadForUser("User2");//starts Thread for User2
startNewThreadForUser("User1");//Replaces Thread for User1
}
Managing running threads is typically done in a thread pool and this is rough in all sorts of ways, but hopefully it's useful.
I can elaborate that mechanism if you want.
Starting a new thread every time that you receive data from a user will lead to running out of resources, besides causing an unnecessary overhead of managing too many threads. Your computer has a limited number of threads that can run at any single time and is limited by your CPU. to find out that number you can use command
Runtime.getRuntime().availableProcessors()
on the other hand, if the jobs that you want to process require a lot of I/O processing, you should launch a few more threads than "Runtime.getRuntime().availableProcessors()", or you will be under-using your CPU.
what I would do is to use a "ExecutorService" which will handle the threads for you (no need to manually start, stop threads). Just start an "ExecutorService" with the total number of threads that you want to execute simultaneously, and then every time that you get more work from a User, submit the new task (as a Callable) to the ExecutorService. The executorService will handle the execution of that task for you, and once it is done it will become available for garbage collection.
for example, see code below:
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MultipleClientsExample {
public static final int TOTAL_THREADS_TO_PROCESS_CUSTOMER_WORK = 4;
public static final Random random = new Random();
public static int customerCounter = 0;
public static void main(String[] args) throws InterruptedException {
MultipleClientsExample multipleClientsExample = new MultipleClientsExample();
multipleClientsExample.doTheWork();
}
private void doTheWork() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(TOTAL_THREADS_TO_PROCESS_CUSTOMER_WORK);
while (customerCounter < 10) {
try {
CustomerInput customerInput = getWorkFromCustomer();
System.out.println("main program. received work from customer: " + customerInput.getCustomerId());
executorService.submit(new WorkToBeDone(customerInput.getCustomerId(), customerInput.getWorkInfo()));
} catch (InterruptedException e) {
break;
}
customerCounter++;
}
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
}
private CustomerInput getWorkFromCustomer() throws InterruptedException {
while (true) {
String customerId = String.valueOf(random.nextInt(10));
CustomerInput customerInput = new CustomerInput(customerId, "work from customer: " + customerId);
return customerInput;
}
}
}
class WorkToBeDone implements Callable<Void> {
private String clientId;
private String workInfo;
public WorkToBeDone(String clientId, String workInfo) {
this.clientId = clientId;
this.workInfo = workInfo;
}
#Override
public Void call() throws Exception {
System.out.println("inside a working thread: it is going to do the work of customer: " + clientId);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("worker processing job from customer: " + clientId + " was interrupted. ending now");
return null;
}
System.out.println("work completed for customer: " + clientId);
return null;
}
}
class CustomerInput {
private String customerId;
private String workInfo;
public CustomerInput(String customerId, String workInfo) {
this.customerId = customerId;
this.workInfo = workInfo;
}
public String getCustomerId() {
return customerId;
}
public String getWorkInfo() {
return workInfo;
}
}
In case you want the ability to cancel a task that has already been submitted to the thread pool, you will have to keep reference of the Future values of each task, and make sure to remove the reference of the tasks that completed and that you cancelled, so they are ready to be garbage collected (otherwise you will have a memory leak).
for example
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class MultipleClientsExample {
public static final int TOTAL_THREADS_TO_PROCESS_CUSTOMER_WORK = 4;
public static int customerCounter = 0;
public static void main(String[] args) throws InterruptedException {
MultipleClientsExample multipleClientsExample = new MultipleClientsExample();
multipleClientsExample.doTheWork();
}
private void doTheWork() throws InterruptedException {
final ExecutorService executorService = Executors.newFixedThreadPool(TOTAL_THREADS_TO_PROCESS_CUSTOMER_WORK);
Map<String, Future<String>> map = new ConcurrentHashMap<>();
while (customerCounter < 11) {
try {
WorkToBeDone workToBeDone = getWorkFromCustomer();
System.out.println("main program. received work from customer: " + workToBeDone.getClientId());
Future<String> resultFuture = executorService.submit(workToBeDone);
map.put(workToBeDone.getClientId(), resultFuture);
} catch (InterruptedException e) {
break;
}
customerCounter++;
}
// cancel job of customer with id: 10
Future<String> resultFuture = map.get("10");
System.out.println("cancelling job of customerId: 10");
resultFuture.cancel(true);
// remove references of all completed jobs
Thread.sleep(2000);
System.out.println("looking for jobs that completed or were cancelled.");
Iterator<Map.Entry<String, Future<String>>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Future<String>> entry = iterator.next();
if (entry.getValue().isCancelled() || entry.getValue().isDone()) {
System.out.println("removing reference of job for customer: " + entry.getKey());
iterator.remove();
}
}
// simpler way to remove entries from map (but doesn't print output of jobs removed from map)
// map.entrySet().removeIf(entry -> entry.getValue().isCancelled() || entry.getValue().isDone());
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
}
private WorkToBeDone getWorkFromCustomer() throws InterruptedException {
String customerId = String.valueOf(customerCounter);
WorkToBeDone workToBeDone = new WorkToBeDone(customerId, "work from customer: " + customerId);
return workToBeDone;
}
}
class WorkToBeDone implements Callable<String> {
private String clientId;
private String workInfo;
public String getClientId() {
return clientId;
}
public WorkToBeDone(String clientId, String workInfo) {
this.clientId = clientId;
this.workInfo = workInfo;
}
#Override
public String call() throws Exception {
System.out.println("inside a working thread: it is going to do the work of customer: " + clientId);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("worker processing job from customer: " + clientId + " was interrupted. ending now");
return clientId;
}
System.out.println("work completed for customer: " + clientId);
return clientId;
}
}
Good afternoon everyone,
I am working on a school project that requires me to use semaphores to control access to resources. From what I have developed so far, they are:
Semaphore 1) Waiting Area - This permits only 15 customers (Threads) to enter the waiting area, else they are rejected from the store (using TryAcquire).
Semaphore 2) ServerQueue - This permits customers (Threads) to use the only 3 servers in the restaurant once in the waiting area.
My Problem: Our professor requires the serverQueue to take the shortest order (IE, the thread with the least amount of burritosOrdered) when in the waitingArea.
Full flow of application:
Main method instantiates a serverQueue (3 servers) and a waitingArea (15 customers)
Main method instantiates and starts 20 customer threads
Each customer (Thread) run function has been overridden to attempt to get in the waiting area
Each customer in the waitingArea tries to access a server in the serverQueue
How can I tell the serverQueue to get the shortest order? Because the threads override the run, I don't have direct access to an array of all the threads to compare their values.
Thank you for taking a look!
Main
public class Main {
private static final int numCustomers = 5;
public static void main(String[] args)
{
ServerQueue serverQueue = new ServerQueue();
WaitingArea waitingArea = new WaitingArea(3, serverQueue);
Thread customers[] = new Thread[numCustomers];
for (int i = 0; i < numCustomers; i++)
{
customers[i] = new Thread(new Customer(waitingArea), "Customer " + i);
}
for (int i = 0; i < numCustomers; i++)
{
customers[i].start();
}
}
}
Customer
import java.util.Date;
import java.util.Random;
// Runnable is an interface that facilitates threads
public class Customer implements Runnable {
// The semaphore
// private ServerQueue serverQueue;
private WaitingArea waitingArea;
public int burritosOrdered;
public int burritosMade = 0;
// Constructor, allow semaphore to be passed/assigned
public Customer(WaitingArea waitingArea) {
this.waitingArea = waitingArea;
Random r = new Random();
this.burritosOrdered = r.nextInt(21);
}
public void setBurritosMade(int newBurritos) {
this.burritosMade += newBurritos;
}
// We must override the run function within Runnable
// The run function is called by threadObject.start();
#Override
public void run() {
waitingArea.seatCustomer(burritosOrdered);
}
}
waitingArea
import java.util.Date;
import java.util.concurrent.Semaphore;
public class WaitingArea {
private Semaphore semaphore;
private ServerQueue serverQueue;
private int maxCustomers;
public WaitingArea(int maxCustomers, ServerQueue serverQueue) {
semaphore = new Semaphore(maxCustomers, true);
this.serverQueue = serverQueue;
this.maxCustomers = maxCustomers;
}
public void seatCustomer(int burritosOrdered)
{
boolean hasPermit = false;
try
{
hasPermit = semaphore.tryAcquire();
if(hasPermit) {
System.out.println(new Date() + " - "
+ Thread.currentThread().getName()
+ " entered store ordering "
+ burritosOrdered + " burritos");
serverQueue.finishOrder();
} else {
System.out.println(new Date() + " - " + Thread.currentThread().getName() + " left due to full shop");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(hasPermit) {
semaphore.release();
System.out.println(new Date() + " - "
+ Thread.currentThread().getName()
+ " left with " + burritosOrdered + " burritos made");
}
}
}
}
serverQueue
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ServerQueue {
// This Semaphore will keep track of no. of servers used at any point.
private final Semaphore semaphore;
// While checking/acquiring a free server out of three available servers, we will use this lock.
private final Lock serverLock;
// This array represents the pool of free server.
private boolean freeServers[];
public ServerQueue() {
semaphore = new Semaphore(1, true);
freeServers = new boolean[1];
serverLock = new ReentrantLock();
// Set all servers to available
for(int i=0;i<freeServers.length;i++) {
freeServers[i] = true;
}
}
public void finishOrder() throws InterruptedException {
try {
System.out.println(semaphore.getClass());
// Decrease the semaphore counter to mark a printer busy
semaphore.acquire();
// Get the server printer
int assignedServer = getServer();
Thread.sleep(3000);
// Print the job
System.out.println(new Date() + " - " + Thread.currentThread().getName()
+ " is getting service from server " + assignedServer);
//Server is done; Free the server to be used by other threads.
releaseServer(assignedServer);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.print(new Date() + " - " + Thread.currentThread().getName() + " has been served\n");
//Increase the semaphore counter back
semaphore.release();
}
}
//Acquire a free server to finish a job
private int getServer() {
int foundServer = -1;
try {
//Get a lock here so that only one thread can go beyond this at a time
serverLock.lock();
//Check which server is free
for (int i=0; i<freeServers.length; i++)
{
//If free server found then mark it busy
if (freeServers[i])
{
foundServer = i;
freeServers[i] = false;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//Allow other threads to check for free servers
serverLock.unlock();
}
return foundServer;
}
//Release the server
private void releaseServer(int i) {
serverLock.lock();
//Mark the server as free
freeServers[i] = true;
serverLock.unlock();
}
}
I have a list of available employees, categorized by their profession (e.g "Programmer", "Tester"),
the amount of each available profession is stored in a Semaphore.
To accomplish a certain task - each done in a different thread - a list of professions if given (e.g. 2 "Programmer", 1 "Manager")
and the task should get them in an all or nothing fashion - if all are available you take all the ones in the list, otherwise wait for everyone to be available.
I have accomplished this by limiting the access for the list itself using BlockingQueue, Semaphore or just lock it manually.
What I'm asking is what is the proper way to do it, and if possible, how to still have the release method be available to other threads.
You need a monitor (http://en.wikipedia.org/wiki/Monitor_%28synchronization%29) to accomplish your task.
It can be realized with a java.util.concurrent.Lock (ReentrantLock) and many Condition's on the lock.
Your problem really got my interest. Quite a fun project. Here's a basic implementation that seems to work for your description. See the bottom for a runnable example to go along with it. It's fairly limited (doesn't support negative acquires, no timeout options, etc etc), but it has just enough to use it, and you could easily extend upon it as necessary.
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 companion:
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();
}
}
}
Here's a complete, working, contrived example that I believe fulfills the outlined requirements.
It tracks the total number of resources available in Semaphores, the actual resources in BlockingQueues and tasks in BlockingQueues.
If it's unable to acquire the required resources for a task immediately, it resubmits the task to the back of the Queue (this could be done other ways, but in this example it's using a bounded thread pool of workers so you wouldn't necessarily want them to wait until the resources are available as this could potentially prevent parallelization of other tasks that may be able to run immediately)
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;
}
}
}