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

org.glassfish.grizzly.threadpool.AbstractThreadPool Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.grizzly.threadpool;

import java.util.*;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.localization.LogMessages;
import org.glassfish.grizzly.memory.MemoryManager;
import org.glassfish.grizzly.memory.ThreadLocalPoolProvider;
import org.glassfish.grizzly.monitoring.MonitoringAware;
import org.glassfish.grizzly.monitoring.MonitoringConfig;
import org.glassfish.grizzly.monitoring.DefaultMonitoringConfig;
import org.glassfish.grizzly.monitoring.MonitoringUtils;
import org.glassfish.grizzly.utils.DelayedExecutor;

/**
 * Abstract {@link java.util.concurrent.ExecutorService} implementation.
 *
 * @author Alexey Stashok
 */
public abstract class AbstractThreadPool extends AbstractExecutorService
        implements Thread.UncaughtExceptionHandler, MonitoringAware {

    private static final Logger logger = Grizzly.logger(AbstractThreadPool.class);
    // Min number of worker threads in a pool
    public static final int DEFAULT_MIN_THREAD_COUNT;
    // Max number of worker threads in a pool
    public static final int DEFAULT_MAX_THREAD_COUNT;

    // "Never stop the thread by timeout" value
    private static final Long NEVER_TIMEOUT = Long.MAX_VALUE;

    static {
        int processorsBasedThreadCount =
                Runtime.getRuntime().availableProcessors();
        DEFAULT_MIN_THREAD_COUNT = processorsBasedThreadCount > 5 ? processorsBasedThreadCount : 5;
        DEFAULT_MAX_THREAD_COUNT = Integer.MAX_VALUE;
    }
    
    // Max number of tasks thread pool can enqueue
    public static final int DEFAULT_MAX_TASKS_QUEUED = -1;
    // Timeout, after which idle thread will be stopped and excluded from pool
    public static final int DEFAULT_IDLE_THREAD_KEEPALIVE_TIMEOUT = 30000;
    
    protected static final Runnable poison = new Runnable() {

        @Override
        public void run() {
        }
    };
    
    protected final Object stateLock = new Object();
    
    protected final Map workers = new HashMap();
    protected volatile boolean running = true;
    protected final ThreadPoolConfig config;
    protected final long transactionTimeoutMillis;
    protected final DelayedExecutor.DelayQueue delayedQueue;

    private static final DelayedExecutor.Resolver transactionResolver =
            new DelayedExecutor.Resolver() {

        @Override
        public boolean removeTimeout(final Worker element) {
            element.transactionExpirationTime = -1;
            return true;
        }

        @Override
        public long getTimeoutMillis(final Worker element) {
            return element.transactionExpirationTime;
        }

        @Override
        public void setTimeoutMillis(final Worker element,
                final long timeoutMillis) {
            element.transactionExpirationTime = timeoutMillis;
        }
    };

    /**
     * ThreadPool probes
     */
    protected final DefaultMonitoringConfig monitoringConfig =
            new DefaultMonitoringConfig(ThreadPoolProbe.class) {

        @Override
        public Object createManagementObject() {
            return createJmxManagementObject();
        }

    };
    
    public AbstractThreadPool(ThreadPoolConfig config) {
        if (config.getMaxPoolSize() < 1) {
            throw new IllegalArgumentException("poolsize < 1");
        }

        this.config = config;
        if (config.getInitialMonitoringConfig().hasProbes()) {
            monitoringConfig.addProbes(config.getInitialMonitoringConfig().getProbes());
        }
        
        if (config.getThreadFactory() == null) {
            config.setThreadFactory(getDefaultThreadFactory());
        }

        transactionTimeoutMillis = config.getTransactionTimeout(TimeUnit.MILLISECONDS);
        final DelayedExecutor transactionMonitor = transactionTimeoutMillis > 0 ?
            config.getTransactionMonitor() : null;

        if (transactionMonitor != null) {
            final DelayedExecutor.Worker transactionWorker =
                    new DelayedExecutor.Worker() {

                @Override
                public boolean doWork(final Worker worker) {
                    worker.t.interrupt();
                    delayedQueue.add(worker, NEVER_TIMEOUT, TimeUnit.MILLISECONDS);
                    return true;
                }
            };
            delayedQueue = transactionMonitor.createDelayQueue(
                    transactionWorker, transactionResolver);
        } else {
            delayedQueue = null;
        }
    }

    /**
     * must hold statelock while calling this method.
     * @param worker
     */
    protected void startWorker(final Worker worker) {
        final Thread thread = config.getThreadFactory().newThread(worker);

        worker.t = thread;
        workers.put(worker, System.currentTimeMillis());
        worker.t.start();
    }

    /**
     * @return the thread pool configuration
     */
    public ThreadPoolConfig getConfig() {
        return config;
    }
    
    /**
     * @return the task {@link Queue}
     */
    public Queue getQueue() {
        return config.getQueue();
    }

    /**
     * @return the number of allocated threads in the thread pool
     */
    public final int getSize() {
        synchronized (stateLock) {
            return workers.size();
        }
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public List shutdownNow() {
        synchronized (stateLock) {
            List drained = new ArrayList();
            if (running) {
                running = false;
                drain(getQueue(), drained);
                for (Runnable task : drained) {
                    onTaskDequeued(task);
                    onTaskCancelled(task);
                }
                poisonAll();
                //try to interrupt their current work so they can get their poison fast
                for (Worker w : workers.keySet()) {
                    w.t.interrupt();
                }

                ProbeNotifier.notifyThreadPoolStopped(this);
            }
            return drained;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void shutdown() {
        synchronized (stateLock) {
            if (running) {
                running = false;
                poisonAll();
                stateLock.notifyAll();
                ProbeNotifier.notifyThreadPoolStopped(this);
            }
        }
    }

    @Override
    public boolean isShutdown() {
        return !running;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isTerminated() {
        synchronized (stateLock) {
            return !running && workers.isEmpty();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean awaitTermination(final long timeout, final TimeUnit unit)
            throws InterruptedException {
        // see {@link java.util.concurrent.ThreadPoolExecutor#awaitTermination(long, TimeUnit)}

        long millis = unit.toMillis(timeout);
        final long timeEnd = System.currentTimeMillis() + millis;

        synchronized (stateLock) {
            if (isTerminated()) {
                return true;
            }

            for (;;) {
                if (millis < 20) {
                    return false;
                }

                stateLock.wait(millis);

                if (isTerminated()) {
                    return true;
                }

                millis = timeEnd - System.currentTimeMillis();
            }
        }
    }
    
    protected void poisonAll() {
        int size = Math.max(config.getMaxPoolSize(), workers.size()) * 4 / 3;
        final Queue q = getQueue();
        while (size-- > 0) {
            q.offer(poison);
        }
    }

    protected static void drain(Queue from,
            Collection to) {
        boolean cont = true;
        while (cont) {
            Runnable r = from.poll();
            if (cont = r != null) {
                //resizable fixedpool can have poison
                //from runtime resize (shrink) operation
                if (r != AbstractThreadPool.poison) {
                    to.add(r);//bypassing pool queuelimit
                }
            }
        }
    }

    protected void validateNewPoolSize(int corePoolsize, int maxPoolSize) {
        if (maxPoolSize < 1) {
            throw new IllegalArgumentException("maxPoolsize < 1 :" + maxPoolSize);
        }
        if (corePoolsize < 1) {
            throw new IllegalArgumentException("corePoolsize < 1 :" + corePoolsize);
        }
        if (corePoolsize > maxPoolSize) {
            throw new IllegalArgumentException("corePoolsize > maxPoolSize: "
                    + corePoolsize + " > " + maxPoolSize);
        }
    }

    /**
     * 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.
     *
     * 

This implementation does nothing, but may be customized in * subclasses. Note: To properly nest multiple overridings, subclasses * should generally invoke super.beforeExecute at the end of * this method. * * @param worker the {@link Worker}, running the the thread t * @param t the thread that will run task r. * @param r the task that will be executed. */ protected void beforeExecute(final Worker worker, final Thread t, final Runnable r) { if (delayedQueue != null) { worker.transactionExpirationTime = System.currentTimeMillis() + transactionTimeoutMillis; } final ClassLoader initial = config.getInitialClassLoader(); if (initial != null) { t.setContextClassLoader(initial); } } /** * Method invoked upon completion of execution of the given Runnable. * This method is invoked by the thread that executed the task. If * non-null, the Throwable is the uncaught RuntimeException * or Error that caused execution to terminate abruptly. * *

Note: When actions are enclosed in tasks (such as * {@link java.util.concurrent.FutureTask}) either explicitly or via methods such as * submit, these task objects catch and maintain * computational exceptions, and so they do not cause abrupt * termination, and the internal exceptions are not * passed to this method. * *

This implementation does nothing, but may be customized in * subclasses. Note: To properly nest multiple overridings, subclasses * should generally invoke super.afterExecute at the * beginning of this method. * * @param worker the {@link Worker}, running the the thread t * @param thread * @param r the runnable that has completed. * @param t the exception that caused termination, or null if * execution completed normally. */ protected void afterExecute(final Worker worker, final Thread thread, final Runnable r, final Throwable t) { if (delayedQueue != null) { worker.transactionExpirationTime = NEVER_TIMEOUT; } } /** *

* This method will be invoked when a the specified {@link Runnable} has * completed execution. *

* * @param task the unit of work that has completed processing */ protected void onTaskCompletedEvent(Runnable task) { ProbeNotifier.notifyTaskCompleted(this, task); } /** * Method is called by {@link Worker}, when it's starting * {@link Worker#run()} method execution, which means, that ThreadPool's * thread is getting active and ready to process tasks. * This method is called from {@link Worker}'s thread. * * @param worker */ protected void onWorkerStarted(final Worker worker) { if (delayedQueue != null) { delayedQueue.add(worker, NEVER_TIMEOUT, TimeUnit.MILLISECONDS); } ProbeNotifier.notifyThreadAllocated(this, worker.t); } /** * Method is called by {@link Worker}, when it's completing * {@link Worker#run()} method execution, which in most cases means, * that ThreadPool's thread will be released. This method is called from * {@link Worker}'s thread. * * @param worker */ protected void onWorkerExit(Worker worker) { synchronized (stateLock) { workers.remove(worker); if (workers.isEmpty()) { // notify awaitTermination threads stateLock.notifyAll(); } } ProbeNotifier.notifyThreadReleased(this, worker.t); } /** * Method is called by AbstractThreadPool, when maximum number of * worker threads is reached and task will need to wait in task queue, until * one of the threads will be able to process it. */ protected void onMaxNumberOfThreadsReached() { ProbeNotifier.notifyMaxNumberOfThreads(this, config.getMaxPoolSize()); } /** * Method is called by a thread pool each time new task has been queued to * a task queue. * * @param task */ protected void onTaskQueued(Runnable task) { ProbeNotifier.notifyTaskQueued(this, task); } /** * Method is called by a thread pool each time a task has been dequeued from * a task queue. * * @param task */ protected void onTaskDequeued(Runnable task) { ProbeNotifier.notifyTaskDequeued(this, task); } /** * Method is called by a thread pool each time a dequeued task has been canceled * instead of being processed. * * @param task */ protected void onTaskCancelled(Runnable task) { ProbeNotifier.notifyTaskCancelled(this, task); } /** * Method is called by a thread pool, when new task could not be added * to a task queue, because task queue is full. * throws {@link RejectedExecutionException} */ protected void onTaskQueueOverflow() { ProbeNotifier.notifyTaskQueueOverflow(this); throw new RejectedExecutionException( "The thread pool's task queue is full, limit: " + config.getQueueLimit()); } /** * {@inheritDoc} */ @Override public MonitoringConfig getMonitoringConfig() { return monitoringConfig; } /** * {@inheritDoc} */ @Override public void uncaughtException(Thread thread, Throwable throwable) { logger.log(Level.WARNING, LogMessages.WARNING_GRIZZLY_THREADPOOL_UNCAUGHT_EXCEPTION(thread), throwable); } Object createJmxManagementObject() { return MonitoringUtils.loadJmxObject( "org.glassfish.grizzly.threadpool.jmx.ThreadPool", this, AbstractThreadPool.class); } protected final ThreadFactory getDefaultThreadFactory() { final AtomicInteger counter = new AtomicInteger(); return new ThreadFactory() { @Override public Thread newThread(Runnable r) { final MemoryManager mm = config.getMemoryManager(); final ThreadLocalPoolProvider threadLocalPoolProvider; if (mm instanceof ThreadLocalPoolProvider) { threadLocalPoolProvider = (ThreadLocalPoolProvider) mm; } else { threadLocalPoolProvider = null; } final DefaultWorkerThread thread = new DefaultWorkerThread(Grizzly.DEFAULT_ATTRIBUTE_BUILDER, config.getPoolName() + '(' + counter.incrementAndGet() + ')', ((threadLocalPoolProvider != null) ? threadLocalPoolProvider.createThreadLocalPool() : null), r); thread.setUncaughtExceptionHandler(AbstractThreadPool.this); thread.setPriority(config.getPriority()); thread.setDaemon(config.isDaemon()); final ClassLoader initial = config.getInitialClassLoader(); if (initial != null) { thread.setContextClassLoader(initial); } return thread; } }; } @Override public String toString() { StringBuilder sb = new StringBuilder(256); sb.append(getClass().getSimpleName()); sb.append(" config: [").append(config.toString()).append("]\r\n"); sb.append(", is-shutdown=").append(isShutdown()); return sb.toString(); } public abstract class Worker implements Runnable { protected Thread t; protected volatile long transactionExpirationTime; @Override public void run() { try { onWorkerStarted(this);//inside try, to ensure balance doWork(); } finally { onWorkerExit(this); } } protected void doWork() { final Thread thread = t; while (true) { try { Thread.interrupted(); final Runnable r = getTask(); if (r == poison || r == null) { return; } onTaskDequeued(r); Throwable error = null; try { beforeExecute(this, thread, r); //inside try. to ensure balance r.run(); onTaskCompletedEvent(r); } catch (Exception e) { error = e; } finally { afterExecute(this, thread, r, error); } } catch (Exception ignore) { } } } protected abstract Runnable getTask() throws InterruptedException; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy