/* * 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 org.apache.jackrabbit.core.query.lucene; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * <code>DynamicPooledExecutor</code> implements an executor, which dynamically * adjusts its maximum number of threads according to the number of available * processors returned by {@link Runtime#availableProcessors()}. */ public class DynamicPooledExecutor implements Executor { /** * Number of instances that access the underlying executor. * Used to automatically shutdown the thread pool when unused. */ private static int instances = 0; /** * The underlying pooled executor. */ private static ThreadPoolExecutor executor = null; /** * The time (in milliseconds) when the pool size was last checked. */ private static long lastCheck; /** * Creates a new DynamicPooledExecutor. */ public DynamicPooledExecutor() { startInstance(); } /** * Adjusts the pool size at most once every second. */ private static synchronized ThreadPoolExecutor adjustPoolSize() { long now = System.currentTimeMillis(); if (lastCheck + 1000 < now) { int n = Runtime.getRuntime().availableProcessors(); if (n != executor.getMaximumPoolSize()) { executor.setMaximumPoolSize(n); } lastCheck = now; } return executor; } /** * Executes the given command. This method will block if all threads in the * pool are busy and return only when the command has been accepted. Care * must be taken, that no deadlock occurs when multiple commands are * scheduled for execution. In general commands should not depend on the * execution of other commands! * * @param command the command to execute. */ public void execute(Runnable command) { ThreadPoolExecutor executor = adjustPoolSize(); if (executor.getMaximumPoolSize() == 1) { // if there is only one processor execute with current thread command.run(); } else { executor.execute(command); } } public void close() { stopInstance(); } private static synchronized void startInstance() { instances++; if (executor == null) { ThreadFactory f = new ThreadFactory() { public Thread newThread(Runnable r) { Thread t = new Thread(r, "DynamicPooledExecutor"); t.setDaemon(true); return t; } }; executor = new ThreadPoolExecutor( 1, Runtime.getRuntime().availableProcessors(), 500, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), f); lastCheck = System.currentTimeMillis(); } } private static synchronized void stopInstance() { instances--; if (instances == 0) { executor.shutdown(); try { executor.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { // ignore and continue } executor = null; } } }