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

org.jboss.threads.QueueExecutor Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2017 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.jboss.threads;

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

import org.jboss.threads.management.BoundedQueueThreadPoolExecutorMBean;
import org.wildfly.common.Assert;

/**
 * An executor which uses a regular queue to hold tasks.  The executor may be tuned at runtime in many ways.
 *
 * @deprecated Use {@link EnhancedQueueExecutor} instead.
 */
@Deprecated
public final class QueueExecutor extends AbstractExecutorService implements BlockingExecutorService, BoundedQueueThreadPoolExecutorMBean, ShutdownListenable {

    private final SimpleShutdownListenable shutdownListenable = new SimpleShutdownListenable();

    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;
    private final DirectExecutor taskExecutor;

    // all protected by poolLock...
    private int coreThreads;
    private int maxThreads;
    private int largestPoolSize;
    private int rejectCount;
    private boolean allowCoreThreadTimeout;
    private long keepAliveTime;
    private TimeUnit keepAliveTimeUnit;
    private boolean blocking;
    private Executor handoffExecutor;

    private int threadCount;
    private Set workers = new HashSet();

    private boolean stop;

    private Queue queue;

    /**
     * Create a new instance.
     *
     * @param coreThreads the number of threads to create before enqueueing tasks
     * @param maxThreads the maximum number of threads to create
     * @param keepAliveTime the amount of time that an idle thread should remain active
     * @param keepAliveTimeUnit the unit of time for {@code keepAliveTime}
     * @param queue the queue to use for tasks
     * @param threadFactory the thread factory to use for new threads
     * @param blocking {@code true} if the executor should block when the queue is full and no threads are available, {@code false} to use the handoff executor
     * @param handoffExecutor the executor which is called when blocking is disabled and a task cannot be accepted, or {@code null} to reject the task
     * @param taskExecutor the executor to use to execute tasks
     */
    public QueueExecutor(final int coreThreads, final int maxThreads, final long keepAliveTime, final TimeUnit keepAliveTimeUnit, final Queue queue, final ThreadFactory threadFactory, final boolean blocking, final Executor handoffExecutor, final DirectExecutor taskExecutor) {
        Assert.checkNotNullParam("threadFactory", threadFactory);
        Assert.checkNotNullParam("queue", queue);
        Assert.checkNotNullParam("keepAliveTimeUnit", keepAliveTimeUnit);
        final Lock lock = this.lock;
        lock.lock();
        try {
            this.threadFactory = threadFactory;
            // configurable...
            this.keepAliveTime = keepAliveTime;
            this.keepAliveTimeUnit = keepAliveTimeUnit;
            this.coreThreads = coreThreads;
            this.maxThreads = maxThreads > coreThreads ? maxThreads : coreThreads;
            this.queue = queue;
            this.blocking = blocking;
            this.handoffExecutor = handoffExecutor;
            this.taskExecutor = taskExecutor;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Create a new instance.
     *
     * @param coreThreads the number of threads to create before enqueueing tasks
     * @param maxThreads the maximum number of threads to create
     * @param keepAliveTime the amount of time that an idle thread should remain active
     * @param keepAliveTimeUnit the unit of time for {@code keepAliveTime}
     * @param queue the queue to use for tasks
     * @param threadFactory the thread factory to use for new threads
     * @param blocking {@code true} if the executor should block when the queue is full and no threads are available, {@code false} to use the handoff executor
     * @param handoffExecutor the executor which is called when blocking is disabled and a task cannot be accepted, or {@code null} to reject the task
     */
    public QueueExecutor(final int coreThreads, final int maxThreads, final long keepAliveTime, final TimeUnit keepAliveTimeUnit, final Queue queue, final ThreadFactory threadFactory, final boolean blocking, final Executor handoffExecutor) {
        this(coreThreads, maxThreads, keepAliveTime, keepAliveTimeUnit, queue, threadFactory, blocking, handoffExecutor, JBossExecutors.directExecutor());
    }

    /**
     * Create a new instance.
     *
     * @param coreThreads the number of threads to create before enqueueing tasks
     * @param maxThreads the maximum number of threads to create
     * @param keepAliveTime the amount of time that an idle thread should remain active
     * @param keepAliveTimeUnit the unit of time for {@code keepAliveTime}
     * @param queueLength the fixed queue length to use for tasks
     * @param threadFactory the thread factory to use for new threads
     * @param blocking {@code true} if the executor should block when the queue is full and no threads are available, {@code false} to use the handoff executor
     * @param handoffExecutor the executor which is called when blocking is disabled and a task cannot be accepted, or {@code null} to reject the task
     */
    public QueueExecutor(final int coreThreads, final int maxThreads, final long keepAliveTime, final TimeUnit keepAliveTimeUnit, final int queueLength, final ThreadFactory threadFactory, final boolean blocking, final Executor handoffExecutor) {
        this(coreThreads, maxThreads, keepAliveTime, keepAliveTimeUnit, new ArrayQueue(queueLength), threadFactory, blocking, handoffExecutor);
    }

    /**
     * Execute a task.
     *
     * @param task the task to execute
     * @throws RejectedExecutionException when a task is rejected by the handoff executor
     * @throws StoppedExecutorException when the executor is terminating
     * @throws ExecutionInterruptedException when blocking is enabled and the current thread is interrupted before a task could be accepted
     */
    public void execute(final Runnable task) throws RejectedExecutionException {
        Assert.checkNotNullParam("task", task);
        final Executor executor;
        final Lock lock = this.lock;
        lock.lock();
        try {
            for (;;) {
                if (stop) {
                    throw Messages.msg.shutDownInitiated();
                }
                // Try core thread first, then queue, then extra thread
                final int count = threadCount;
                if (count < coreThreads) {
                    startNewThread(task);
                    threadCount = count + 1;
                    return;
                }
                // next queue...
                final Queue queue = this.queue;
                if (queue.offer(task)) {
                    enqueueCondition.signal();
                    return;
                }
                // extra threads?
                if (count < maxThreads) {
                    startNewThread(task);
                    threadCount = count + 1;
                    return;
                }
                if (blocking) {
                    try {
                        removeCondition.await();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        throw Messages.msg.executionInterrupted();
                    }
                } else {
                    // delegate the task outside of the lock.
                    rejectCount++;
                    executor = handoffExecutor;
                    break;
                }
            }
        } finally {
            lock.unlock();
        }
        if (executor != null) {
            executor.execute(task);
        } else {
            throw Messages.msg.executionRejected();
        }
        return;
    }

    /**
     * Execute a task, blocking until it can be accepted, or until the calling thread is interrupted.
     *
     * @param task the task to submit
     *
     * @throws org.jboss.threads.StoppedExecutorException if the executor was shut down before the task was accepted
     * @throws org.jboss.threads.ThreadCreationException if a thread could not be created for some reason
     * @throws java.util.concurrent.RejectedExecutionException if execution is rejected for some other reason
     * @throws InterruptedException if the current thread was interrupted before the task could be accepted
     * @throws NullPointerException if command is {@code null}
     */
    public void executeBlocking(final Runnable task) throws RejectedExecutionException, InterruptedException {
        Assert.checkNotNullParam("task", task);
        final Lock lock = this.lock;
        lock.lock();
        try {
            for (;;) {
                if (stop) {
                    throw Messages.msg.shutDownInitiated();
                }
                // Try core thread first, then queue, then extra thread
                final int count = threadCount;
                if (count < coreThreads) {
                    startNewThread(task);
                    threadCount = count + 1;
                    return;
                }
                // next queue...
                final Queue queue = this.queue;
                if (queue.offer(task)) {
                    enqueueCondition.signal();
                    return;
                }
                // extra threads?
                if (count < maxThreads) {
                    startNewThread(task);
                    threadCount = count + 1;
                    return;
                }
                removeCondition.await();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * Execute a task, blocking until it can be accepted, a timeout elapses, or the calling thread is interrupted.
     *
     * @param task the task to submit
     * @param timeout the amount of time to wait
     * @param unit the unit of time
     *
     * @throws org.jboss.threads.ExecutionTimedOutException if the timeout elapsed before a task could be accepted
     * @throws org.jboss.threads.StoppedExecutorException if the executor was shut down before the task was accepted
     * @throws org.jboss.threads.ThreadCreationException if a thread could not be created for some reason
     * @throws java.util.concurrent.RejectedExecutionException if execution is rejected for some other reason
     * @throws InterruptedException if the current thread was interrupted before the task could be accepted
     * @throws NullPointerException if command is {@code null}
     */
    public void executeBlocking(final Runnable task, final long timeout, final TimeUnit unit) throws RejectedExecutionException, InterruptedException {
        Assert.checkNotNullParam("task", task);
        long now = System.currentTimeMillis();
        final long deadline = now + unit.toMillis(timeout);
        if (deadline < 0L) {
            executeBlocking(task);
            return;
        }
        final Lock lock = this.lock;
        lock.lock();
        try {
            for (;;) {
                if (stop) {
                    throw Messages.msg.shutDownInitiated();
                }
                // Try core thread first, then queue, then extra thread
                final int count = threadCount;
                if (count < coreThreads) {
                    startNewThread(task);
                    threadCount = count + 1;
                    return;
                }
                // next queue...
                final Queue queue = this.queue;
                if (queue.offer(task)) {
                    enqueueCondition.signal();
                    return;
                }
                // extra threads?
                if (count < maxThreads) {
                    startNewThread(task);
                    threadCount = count + 1;
                    return;
                }
                final long remaining = deadline - now;
                if (remaining <= 0L) {
                    throw Messages.msg.executionTimedOut();
                }
                removeCondition.await(remaining, TimeUnit.MILLISECONDS);
                now = System.currentTimeMillis();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * Execute a task, without blocking.
     *
     * @param task the task to submit
     *
     * @throws org.jboss.threads.StoppedExecutorException if the executor was shut down before the task was accepted
     * @throws org.jboss.threads.ThreadCreationException if a thread could not be created for some reason
     * @throws java.util.concurrent.RejectedExecutionException if execution is rejected for some other reason
     * @throws NullPointerException if command is {@code null}
     */
    public void executeNonBlocking(final Runnable task) throws RejectedExecutionException {
        Assert.checkNotNullParam("task", task);
        final Executor executor;
        final Lock lock = this.lock;
        lock.lock();
        try {
            if (stop) {
                throw Messages.msg.shutDownInitiated();
            }
            // Try core thread first, then queue, then extra thread
            final int count = threadCount;
            if (count < coreThreads) {
                startNewThread(task);
                threadCount = count + 1;
                return;
            }
            // next queue...
            final Queue queue = this.queue;
            if (queue.offer(task)) {
                enqueueCondition.signal();
                return;
            }
            // extra threads?
            if (count < maxThreads) {
                startNewThread(task);
                threadCount = count + 1;
                return;
            }
            // delegate the task outside of the lock.
            rejectCount++;
            executor = handoffExecutor;
        } finally {
            lock.unlock();
        }
        if (executor != null) {
            executor.execute(task);
        }
        return;
    }

    /** {@inheritDoc} */
    public void shutdown() {
        boolean callShutdownListener = false;
        final Lock lock = this.lock;
        lock.lock();
        try {
            if (! stop) {
                stop = true;
                // wake up the whole town
                removeCondition.signalAll();
                enqueueCondition.signalAll();
                if (workers.isEmpty()) {
                    callShutdownListener = true;
                } else {
                    for (Thread worker : workers) {
                        worker.interrupt();
                    }
                }
            }
        } finally {
            lock.unlock();
            if (callShutdownListener)
                shutdownListenable.shutdown();
        }
    }

    /** {@inheritDoc} */
    public List shutdownNow() {
        boolean callShutdownListener = false;
        final Lock lock = this.lock;
        lock.lock();
        try {
            stop = true;
            removeCondition.signalAll();
            enqueueCondition.signalAll();
            if (workers.isEmpty()) {
                callShutdownListener = true;
            } else {
                for (Thread worker : workers) {
                    worker.interrupt();
                }
            }
            final Queue queue = this.queue;
            final ArrayList list = new ArrayList(queue);
            queue.clear();
            return list;
        } finally {
            lock.unlock();
            if (callShutdownListener)
                shutdownListenable.shutdown();
        }
    }

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

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

    /** {@inheritDoc} */
    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 Messages.msg.cannotAwaitWithin();
            }
            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();
        }
    }

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

    /** {@inheritDoc} */
    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();
        }
    }

    /** {@inheritDoc} */
    public int getCoreThreads() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return coreThreads;
        } finally {
            lock.unlock();
        }
    }

    /** {@inheritDoc} */
    public void setCoreThreads(final int coreThreads) {
        final Lock lock = this.lock;
        lock.lock();
        try {
            final int oldLimit = this.coreThreads;
            if (maxThreads < coreThreads) {
                // don't let the max thread limit be less than the core thread limit.
                // the called method will signal as needed
                setMaxThreads(coreThreads);
            } else if (oldLimit < coreThreads) {
                // 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 > coreThreads) {
                // 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.coreThreads = coreThreads;
        } finally {
            lock.unlock();
        }
    }

    /** {@inheritDoc} */
    public int getMaxThreads() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return maxThreads;
        } finally {
            lock.unlock();
        }
    }

    /** {@inheritDoc} */
    public void setMaxThreads(final int maxThreads) {
        final Lock lock = this.lock;
        lock.lock();
        try {
            final int oldLimit = this.maxThreads;
            if (maxThreads < coreThreads) {
                // don't let the max thread limit be less than the core thread limit.
                // the called method will signal as needed
                setCoreThreads(maxThreads);
            } else if (oldLimit < maxThreads) {
                // 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 > maxThreads) {
                // 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.maxThreads = maxThreads;
        } finally {
            lock.unlock();
        }
    }

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

    /**
     * Set the keep-alive time to the given amount of time.
     *
     * @param keepAliveTime the amount of time
     * @param keepAliveTimeUnit the unit of time
     */
    public void setKeepAliveTime(final long keepAliveTime, final TimeUnit keepAliveTimeUnit) {
        Assert.checkNotNullParam("keepAliveTimeUnit", keepAliveTimeUnit);
        Assert.checkMinimumParameter("keepAliveTime", 0L, keepAliveTime);
        final Lock lock = this.lock;
        lock.lock();
        try {
            this.keepAliveTime = keepAliveTimeUnit.convert(keepAliveTime, TimeUnit.MILLISECONDS);
        } finally {
            lock.unlock();
        }
    }

    /** {@inheritDoc} */
    public void setKeepAliveTime(final long milliseconds) {
        setKeepAliveTime(milliseconds, TimeUnit.MILLISECONDS);
    }

    /**
     * Determine whether this thread pool executor is set to block when a task cannot be accepted immediately.
     *
     * @return {@code true} if blocking is enabled, {@code false} if the handoff executor is used
     */
    public boolean isBlocking() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return blocking;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Set whether this thread pool executor should be set to block when a task cannot be accepted immediately.
     *
     * @param blocking {@code true} if blocking is enabled, {@code false} if the handoff executor is used
     */
    public void setBlocking(boolean blocking) {
        final Lock lock = this.lock;
        lock.lock();
        try {
            this.blocking = blocking;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Get the handoff executor which is called when a task cannot be accepted immediately.
     *
     * @return the handoff executor
     */
    public Executor getHandoffExecutor() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return handoffExecutor;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Set the handoff executor which is called when a task cannot be accepted immediately.
     *
     * @param handoffExecutor the handoff executor
     */
    public void setHandoffExecutor(final Executor handoffExecutor) {
        final Lock lock = this.lock;
        lock.lock();
        try {
            this.handoffExecutor = handoffExecutor;
        } finally {
            lock.unlock();
        }
    }

    /** {@inheritDoc} */
    public  void addShutdownListener(final EventListener shutdownListener, final A attachment) {
        shutdownListenable.addShutdownListener(shutdownListener, attachment);
    }

    // call with lock held!
    private void startNewThread(final Runnable task) {
        final Thread thread = threadFactory.newThread(new Worker(task));
        if (thread == null) {
            throw Messages.msg.noThreadCreated();
        }
        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 = coreThreads;
                    final boolean allowCoreThreadTimeout = this.allowCoreThreadTimeout;
                    if (stop || threadCount > maxThreads) {
                        // too many threads.  Handle a task if there is one, otherwise exit
                        return pollTask();
                    } else if (allowCoreThreadTimeout || threadCount > coreThreadLimit) {
                        final TimeUnit timeUnit = keepAliveTimeUnit;
                        final long time = keepAliveTime;
                        final long remaining = time - timeUnit.convert(elapsed, TimeUnit.MILLISECONDS);
                        if (remaining > 0L) {
                            try {
                                enqueueCondition.await(remaining, timeUnit);
                            } catch (InterruptedException e) {
                                intr = true;
                            }
                        } else {
                            // the timeout has expired
                            return pollTask();
                        }
                    } else {
                        // 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;
                        }
                    }
                    task = queue.poll();
                    if (task != null) {
                        removeCondition.signal();
                        return task;
                    }
                    elapsed = System.currentTimeMillis() - start;
                }
            } finally {
                if (intr) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    /** {@inheritDoc} */
    public int getCurrentThreadCount() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return workers.size();
        } finally {
            lock.unlock();
        }
    }

    /** {@inheritDoc} */
    public int getLargestThreadCount() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            return largestPoolSize;
        } finally {
            lock.unlock();
        }
    }

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

    /** {@inheritDoc} */
    public int getQueueSize() {
        return this.queue.size();
    }

    private void runTask(Runnable task) {
        if (task != null) try {
            taskExecutor.execute(task);
        } catch (Throwable t) {
            Messages.msg.executionFailed(t, task);
        }
    }

    private class Worker implements Runnable {

        private volatile Runnable first;

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

        public void run() {
            final Lock lock = QueueExecutor.this.lock;
            try {
                Runnable task = first;
                // Release reference to task
                first = null;
                runTask(task);
                for (;;) {
                    // don't hang on to task while we possibly block waiting for the next one
                    task = null;
                    lock.lock();
                    try {
                        if (stop) {
                            // drain queue
                            if ((task = pollTask()) == null) {
                                return;
                            }
                            Thread.currentThread().interrupt();
                        } else {
                            // get next task
                            if ((task = takeTask()) == null) {
                                return;
                            }
                        }
                    } finally {
                        lock.unlock();
                    }
                    runTask(task);
                    Thread.interrupted();
                }
            } finally {
                boolean last = false;
                lock.lock();
                try {
                    workers.remove(Thread.currentThread());
                    last = stop && workers.isEmpty();
                } finally {
                    lock.unlock();
                }
                if (last) {
                    shutdownListenable.shutdown();
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy