Package org.jboss.threads

Source Code of org.jboss.threads.SimpleQueueExecutor$Worker

/*
* JBoss, Home of Professional Open Source
* Copyright 2008, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.threads;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ExecutorService;
import java.util.Queue;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

/**
* An executor which uses a regular queue to hold tasks.  The executor may be tuned at runtime in many ways.
*/
public final class SimpleQueueExecutor extends AbstractExecutorService implements ExecutorService, ThreadPoolExecutorMBean {
    private final String name;
    private final Lock lock = new ReentrantLock();
    // signal when a task is written to the queue
    private final Condition enqueueCondition = lock.newCondition();
    // signal when the queue is read
    private final Condition removeCondition = lock.newCondition();
    // signalled when threads terminate
    private final Condition threadExitCondition = lock.newCondition();
    private final ThreadFactory threadFactory;

    // all protected by poolLock...
    private int corePoolSize;
    private int maxPoolSize;
    private int largestPoolSize;
    private int rejectCount;
    private boolean allowCoreThreadTimeout;
    private long keepAliveTime;
    private TimeUnit keepAliveTimeUnit;
    private RejectionPolicy rejectionPolicy;
    private Executor handoffExecutor;

    private int threadCount;
    private Set<Thread> workers = new HashSet<Thread>();

    private boolean stop;
    private boolean interrupt;

    private Queue<Runnable> queue;

    public SimpleQueueExecutor(final String name, final int corePoolSize, final int maxPoolSize, final long keepAliveTime, final TimeUnit keepAliveTimeUnit, final Queue<Runnable> queue, final ThreadFactory threadFactory, final RejectionPolicy rejectionPolicy, final Executor handoffExecutor) {
        this.name = name;
        if (threadFactory == null) {
            throw new NullPointerException("threadFactory is null");
        }
        if (queue == null) {
            throw new NullPointerException("queue is null");
        }
        if (keepAliveTimeUnit == null) {
            throw new NullPointerException("keepAliveTimeUnit is null");
        }
        if (rejectionPolicy == null) {
            throw new NullPointerException("rejectionPolicy is null");
        }
        if (rejectionPolicy == RejectionPolicy.HANDOFF && handoffExecutor == null) {
            throw new NullPointerException("handoffExecutor is null");
        }
        final Lock lock = this.lock;
        lock.lock();
        try {
            this.threadFactory = threadFactory;
            // configurable...
            this.keepAliveTime = keepAliveTime;
            this.keepAliveTimeUnit = keepAliveTimeUnit;
            this.corePoolSize = corePoolSize;
            this.maxPoolSize = maxPoolSize;
            this.queue = queue;
            this.rejectionPolicy = rejectionPolicy;
            this.handoffExecutor = handoffExecutor;
        } finally {
            lock.unlock();
        }
    }

    public void execute(final Runnable task) throws RejectedExecutionException {
        final Lock lock = this.lock;
        try {
            lock.lockInterruptibly();
            try {
                for (;;) {
                    if (stop) {
                        throw new RejectedExecutionException("Executor is stopped");
                    }
                    // Try core thread first, then queue, then extra thread
                    final int count = threadCount;
                    if (count < corePoolSize) {
                        startNewThread(task);
                        threadCount = count + 1;
                        return;
                    }
                    // next queue...
                    final Queue<Runnable> queue = this.queue;
                    if (queue.offer(task)) {
                        enqueueCondition.signal();
                        return;
                    }
                    // extra threads?
                    if (count < maxPoolSize) {
                        startNewThread(task);
                        threadCount = count + 1;
                        return;
                    }
                    rejectCount++;
                    // how to reject the task...
                    switch (rejectionPolicy) {
                        case ABORT:
                            throw new RejectedExecutionException("Executor is busy");
                        case BLOCK:
                            removeCondition.await();
                            break;
                        case DISCARD:
                            return;
                        case DISCARD_OLDEST:
                            if (queue.poll() != null) {
                                queue.add(task);
                                enqueueCondition.signal();
                            }
                            return;
                        case HANDOFF:
                            handoffExecutor.execute(task);
                            return;
                    }
                }
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RejectedExecutionException("Thread interrupted");
        }
    }

    public void shutdown() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            if (! stop) {
                stop = true;
                // wake up the whole town
                removeCondition.signalAll();
                enqueueCondition.signalAll();
            }
        } finally {
            lock.unlock();
        }
    }

    public List<Runnable> shutdownNow() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            stop = true;
            interrupt = true;
            removeCondition.signalAll();
            enqueueCondition.signalAll();
            for (Thread worker : workers) {
                worker.interrupt();
            }
            final Queue<Runnable> queue = this.queue;
            final ArrayList<Runnable> list = new ArrayList<Runnable>(queue);
            queue.clear();
            return list;
        } finally {
            lock.unlock();
        }
    }

    public boolean isShutdown() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return stop;
        } finally {
            lock.unlock();
        }
    }

    public boolean isTerminated() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return stop && threadCount == 0;
        } finally {
            lock.unlock();
        }
    }

    public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
        final Lock lock = this.lock;
        lock.lockInterruptibly();
        try {
            if (workers.contains(Thread.currentThread())) {
                throw new IllegalStateException("Cannot await termination of a thread pool from one of its threads");
            }
            final long start = System.currentTimeMillis();
            long elapsed = 0L;
            while (! stop && threadCount > 0) {
                final long remaining = timeout - elapsed;
                if (remaining <= 0) {
                    return false;
                }
                threadExitCondition.await(remaining, unit);
                elapsed = unit.convert(System.currentTimeMillis() - start, TimeUnit.MILLISECONDS);
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

    public boolean isAllowCoreThreadTimeout() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return allowCoreThreadTimeout;
        } finally {
            lock.unlock();
        }
    }

    public void setAllowCoreThreadTimeout(boolean allowCoreThreadTimeout) {
        final Lock lock = this.lock;
        lock.lock();
        try {
            this.allowCoreThreadTimeout = allowCoreThreadTimeout;
            if (allowCoreThreadTimeout) {
                // wake everyone up so core threads can time out
                enqueueCondition.signalAll();
            }
        } finally {
            lock.unlock();
        }
    }

    public int getCorePoolSize() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return corePoolSize;
        } finally {
            lock.unlock();
        }
    }

    public void setCorePoolSize(final int corePoolSize) {
        final Lock lock = this.lock;
        lock.lock();
        try {
            final int oldLimit = this.corePoolSize;
            if (maxPoolSize < corePoolSize) {
                // don't let the max thread limit be less than the core thread limit.
                // the called method will signal as needed
                setMaxPoolSize(corePoolSize);
            } else if (oldLimit < corePoolSize) {
                // we're growing the number of core threads
                // therefore signal anyone waiting to add tasks; there might be more threads to add
                removeCondition.signalAll();
            } else if (oldLimit > corePoolSize) {
                // we're shrinking the number of core threads
                // therefore signal anyone waiting to remove tasks so the pool can shrink properly
                enqueueCondition.signalAll();
            } else {
                // we aren't changing anything...
                return;
            }
            this.corePoolSize = corePoolSize;
        } finally {
            lock.unlock();
        }
    }

    public int getMaxPoolSize() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return maxPoolSize;
        } finally {
            lock.unlock();
        }
    }

    public void setMaxPoolSize(final int maxPoolSize) {
        final Lock lock = this.lock;
        lock.lock();
        try {
            final int oldLimit = this.maxPoolSize;
            if (maxPoolSize < corePoolSize) {
                // don't let the max thread limit be less than the core thread limit.
                // the called method will signal as needed
                setCorePoolSize(maxPoolSize);
            } else if (oldLimit < maxPoolSize) {
                // we're growing the number of extra threads
                // therefore signal anyone waiting to add tasks; there might be more threads to add
                removeCondition.signalAll();
            } else if (oldLimit > maxPoolSize) {
                // we're shrinking the number of extra threads
                // therefore signal anyone waiting to remove tasks so the pool can shrink properly
                enqueueCondition.signalAll();
            } else {
                // we aren't changing anything...
                return;
            }
            this.maxPoolSize = maxPoolSize;
        } finally {
            lock.unlock();
        }
    }

    public long getKeepAliveTime() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return keepAliveTime;
        } finally {
            lock.unlock();
        }
    }

    public void setKeepAliveTime(final long keepAliveTime, final TimeUnit keepAliveTimeUnit) {
        if (keepAliveTimeUnit == null) {
            throw new NullPointerException("keepAliveTimeUnit is null");
        }
        if (keepAliveTime < 0L) {
            throw new IllegalArgumentException("keepAliveTime is less than zero");
        }
        final Lock lock = this.lock;
        lock.lock();
        try {
            this.keepAliveTime = keepAliveTimeUnit.convert(keepAliveTime, TimeUnit.MILLISECONDS);
        } finally {
            lock.unlock();
        }
    }

    public void setKeepAliveTime(final long milliseconds) {
        setKeepAliveTime(milliseconds, TimeUnit.MILLISECONDS);
    }

    public RejectionPolicy getRejectionPolicy() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return rejectionPolicy;
        } finally {
            lock.unlock();
        }
    }

    public void setRejectionPolicy(final RejectionPolicy newPolicy, final Executor handoffExecutor) {
        if (newPolicy == null) {
            throw new NullPointerException("rejectionPolicy is null");
        }
        if (newPolicy == RejectionPolicy.HANDOFF && handoffExecutor == null) {
            throw new NullPointerException("handoffExecutor is null");
        }
        final Lock lock = this.lock;
        lock.lock();
        try {
            if (rejectionPolicy == RejectionPolicy.BLOCK && newPolicy != RejectionPolicy.BLOCK) {
                // there could be blocking .execute() calls out there; give them a nudge
                removeCondition.signalAll();
            }
            rejectionPolicy = newPolicy;
            this.handoffExecutor = handoffExecutor;
        } finally {
            lock.unlock();
        }
    }

    // container lifecycle methods
    public void stop() {
        shutdown();
        try {
            awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
            // todo log if fails?
        } catch (InterruptedException e) {
            // todo log it?
            Thread.currentThread().interrupt();
        }
    }

    public void destroy() {
        // todo is this the right behavior?
        shutdownNow();
        try {
            awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
            // todo log if fails?
        } catch (InterruptedException e) {
            // todo log it?
            Thread.currentThread().interrupt();
        }
    }

    // call with lock held!
    private void startNewThread(final Runnable task) {
        final Thread thread = threadFactory.newThread(new Worker(task));
        workers.add(thread);
        final int size = workers.size();
        if (size > largestPoolSize) {
            largestPoolSize = size;
        }
        thread.start();
    }

    // call with lock held!
    private Runnable pollTask() {
        final Runnable task = queue.poll();
        if (task != null) {
            removeCondition.signal();
            return task;
        } else {
            if (-- threadCount == 0) {
                threadExitCondition.signalAll();
            }
            return null;
        }
    }

    // call with lock held!
    private Runnable takeTask() {
        final Condition removeCondition = this.removeCondition;
        Runnable task = queue.poll();
        if (task != null) {
            removeCondition.signal();
            return task;
        } else {
            final Condition enqueueCondition = this.enqueueCondition;
            final long start = System.currentTimeMillis();
            boolean intr = Thread.interrupted();
            try {
                long elapsed = 0L;
                for (;;) {
                    // these parameters may change on each iteration
                    final int threadCount = this.threadCount;
                    final int coreThreadLimit = corePoolSize;
                    final boolean allowCoreThreadTimeout = this.allowCoreThreadTimeout;
                    if (stop || threadCount > maxPoolSize) {
                        // too many threads.  Handle a task if there is one, otherwise exit
                        return pollTask();
                    } else if (!allowCoreThreadTimeout && threadCount < coreThreadLimit) {
                        // ignore timeout until we are not a core thread or until core threads are allowed to time out
                        try {
                            enqueueCondition.await();
                        } catch (InterruptedException e) {
                            intr = true;
                        }
                    } else {
                        final TimeUnit timeUnit = keepAliveTimeUnit;
                        final long time = keepAliveTime;
                        final long remaining = time - timeUnit.convert(elapsed, TimeUnit.MILLISECONDS);
                        if (remaining <= 0L && (allowCoreThreadTimeout || threadCount > coreThreadLimit)) {
                            // the timeout has expired
                            return pollTask();
                        }
                        try {
                            enqueueCondition.await(remaining, timeUnit);
                        } catch (InterruptedException e) {
                            intr = true;
                        }
                    }
                    task = queue.poll();
                    if (task != null) {
                        removeCondition.signal();
                        return task;
                    }
                    elapsed = System.currentTimeMillis() - start;
                }
            } finally {
                if (intr) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    public String getName() {
        return name;
    }

    public int getCurrentPoolSize() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return workers.size();
        } finally {
            lock.unlock();
        }
    }

    public int getLargestPoolSize() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return largestPoolSize;
        } finally {
            lock.unlock();
        }
    }

    public int getRejectedCount() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return rejectCount;
        } finally {
            lock.unlock();
        }
    }

    private class Worker implements Runnable {

        private Runnable first;

        public Worker(final Runnable command) {
            first = command;
        }

        public void run() {
            final Lock lock = SimpleQueueExecutor.this.lock;
            Runnable task = first;
            // Release reference to task
            first = null;
            lock.lock();
            try {
                for (;;) {
                    if (task != null) {
                        try {
                            if (interrupt) {
                                Thread.currentThread().interrupt();
                            }
                            lock.unlock();
                            try {
                                task.run();
                            } finally {
                                // this is OK because it's in the finally block after lock.unlock()
                                //noinspection LockAcquiredButNotSafelyReleased
                                lock.lock();
                            }
                        } catch (Throwable t) {
                            // todo - log the exception perhaps
                        }
                        // don't hang on to task while we possibly block down below
                        task = null;
                    }
                    if (stop) {
                        // drain queue
                        if ((task = pollTask()) == null) {
                            return;
                        }
                    } else {
                        // get next task
                        if ((task = takeTask()) == null) {
                            return;
                        }
                    }
                }
            } finally {
                workers.remove(Thread.currentThread());
                lock.unlock();
            }
        }
    }
}
TOP

Related Classes of org.jboss.threads.SimpleQueueExecutor$Worker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.