All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.uci.jforestsx.util.concurrency.BlockingThreadPoolExecutor Maven / Gradle / Ivy

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package edu.uci.jforestsx.util.concurrency;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * This is a blocking version of Java's ThreadPoolExecutor.
 * It makes sure that the number of concurrently running threads
 * does not exceed the poolSize.
 * 
 * This code is based on ideas presented at:
 * http://today.java.net/pub/a/today/2008/10/23/creating-a-notifying-blocking-thread-pool-executor.html
 */
public class BlockingThreadPoolExecutor extends ThreadPoolExecutor {

	private Semaphore semaphore;
	private AtomicInteger tasksInProcess = new AtomicInteger();
	private final Lock lock = new ReentrantLock();
	private final Condition done = lock.newCondition();

	private static BlockingThreadPoolExecutor instance;

	public synchronized static void init(int poolSize) {
		if (instance != null) {
			return;
		}
		instance = new BlockingThreadPoolExecutor(poolSize);
	}

	public static BlockingThreadPoolExecutor getInstance() {
		return instance;
	}

	private BlockingThreadPoolExecutor(int poolSize) {
		super(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
		semaphore = new Semaphore(poolSize);
	}

	@Override
	public void execute(Runnable task) {
		boolean acquired = false;
		do {
			try {
				semaphore.acquire();
				acquired = true;
			} catch (InterruptedException e) {
				// wait forever!
			}
		} while (!acquired);

		tasksInProcess.incrementAndGet();

		try {
			super.execute(task);
		} catch (RuntimeException e) {
			// specifically, handle RejectedExecutionException
			tasksInProcess.decrementAndGet();
			semaphore.release();
			throw e;
		} catch (Error e) {
			tasksInProcess.decrementAndGet();
			semaphore.release();
			throw e;
		}
	}

	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		super.afterExecute(r, t);
		synchronized (this) {
			tasksInProcess.decrementAndGet();
			if (tasksInProcess.intValue() == 0) {
				lock.lock(); // MUST lock!
				try {
					done.signalAll();
				} finally {
					lock.unlock(); // unlock even in case of an exception
				}
			}
		}
		semaphore.release();
	}

	public void await() {
		try {
			lock.lock();
			try {
				while (tasksInProcess.get() > 0) { // avoid signaling on 'spuriously' wake-up
					done.await();
				}
			} finally {
				lock.unlock(); // unlock even in case of an exception
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy