Java - 平衡的ThreadPoolExecutor公平地为并行请求提供线程

时间:2017-01-09 10:57:49

标签: java multithreading threadpoolexecutor

我开发了一段多线程的代码。此代码在Web应用程序中调用,因此可能由多个线程(请求)并行执行。为了控制这段代码将要创建的线程数(通过几个并行请求调用),我使用静态共享ThreadPoolExecutor(Executors.newFixedThreadPool(nbOfThreads))。所以我确信这段代码永远不会创建超过nbOfThreads个线程。为了遵循给定请求中涉及的任务并等待它们完成,我为每个请求使用CompletionService。

现在,我希望在池的线程被赋予请求的方式中有一点“公平”(不确定这是好词)。 使用默认的固定ThreadPoolExecutor,等待队列为LinkedBlockingQueue。它根据到达顺序(FIFO)向Executor提供任务。想象一下,池核心大小为100个线程。第一个请求很大,涉及创建150个任务。因此,它将使池完整并将50个任务放入等待队列中。如果第二个小请求在1秒后到达,即使它只需要池中的2个线程,它也必须等待第一个大请求创建的所有150个任务在处理之前完成。

如何使池公平均匀地为每个请求提供线程?在第一次查询的所有50个等待任务之后,如何使第二个请求的2个任务不等待?

我的想法是开发一个BlockingQueue的个人实现来提供给ThreadPoolExecutor。此BlockingQueue将存储由创建它们的请求分类的等待任务(在具有密钥中的请求的id的后备Map中以及以值存储请求的任务的LinkedBlockingQueue)。然后当ThreadPoolExecutor takepoll队列中的新任务时,队列每次都会从不同的请求中给出一个任务......这是正确的方法吗?用例对我来说似乎很常见。我很惊讶我必须自己实施这些定制和乏味的东西。这就是为什么我认为我可能是错的,并且存在一个众所周知的最佳实践来做到这一点。

这是我做的代码。它有效,但仍然想知道这是否是正确的方法。

public class TestThreadPoolExecutorWithTurningQueue {

    private final static Logger logger = LogManager.getLogger();

    private static ThreadPoolExecutor executorService;

    int nbRequest = 4;

    int nbThreadPerRequest = 8;

    int threadPoolSize = 5;

    private void init() {
        executorService = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS,
                new CategoryBlockingQueue<Runnable>()// my custom blocking queue storing waiting tasks per request
                //new LinkedBlockingQueue<Runnable>()
        );
    }

    @Test
    public void test() throws Exception {
        init();
        // Parallel requests arriving
        ExecutorService tomcat = Executors.newFixedThreadPool(nbRequest);
        for (int i = 0; i < nbRequest; i++) {
            Thread.sleep(10);
            final int finalI = i;
            tomcat.execute(new Runnable() {
                @Override
                public void run() {
                    request(finalI);
                }
            });
        }
        tomcat.shutdown();
        tomcat.awaitTermination(1, TimeUnit.DAYS);
    }

    // Code executed by each request
    // Code multi-threaded using a single shared ThreadPoolExecutor to keep the 
    // number of threads under control
    public void request(final int requestId) {
        final List<Future<Object>> futures = new ArrayList<>();
        CustomCompletionService<Object> completionService = new CustomCompletionService<>(executorService);
        for (int j = 0; j < nbThreadPerRequest; j++) {
            final int finalJ = j;
            futures.add(completionService.submit(new CategoryRunnable(requestId) {
                @Override
                public void run() {
                    logger.debug("thread " + finalJ + " of request " + requestId);
                    try {
                        // here should come the useful things to be done
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, null));
        }
        // Wait fot completion of all the tasks of the request
        // If a task threw an exception, cancel the other tasks of the request
        for (int j = 0; j < nbThreadPerRequest; j++) {
            try {
                completionService.take().get();
            } catch (Exception e) {
                // Cancel the remaining tasks
                for (Future<Object> future : futures) {
                    future.cancel(true);
                }

                // Get the underlying exception
                Exception toThrow = e;
                if (e instanceof ExecutionException) {
                    ExecutionException ex = (ExecutionException) e;
                    toThrow = (Exception) ex.getCause();
                }
                throw new RuntimeException(toThrow);
            }
        }
    }

    public class CustomCompletionService<V> implements CompletionService<V> {

        private final Executor executor;

        private final BlockingQueue<Future<V>> completionQueue;

        public CustomCompletionService(Executor executor) {
            if (executor == null)
                throw new NullPointerException();
            this.executor = executor;
            this.completionQueue = new LinkedBlockingQueue<Future<V>>();
        }

        private RunnableFuture<V> newTaskFor(Callable<V> task) {
            return new FutureTask<V>(task);
        }

        private RunnableFuture<V> newTaskFor(Runnable task, V result) {
            return new FutureTask<V>(task, result);
        }

        public Future<V> submit(CategoryCallable<V> task) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<V> f = newTaskFor(task);
            executor.execute(new CategorizedQueueingFuture(f, task.getCategory()));
            return f;
        }

        public Future<V> submit(CategoryRunnable task, V result) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<V> f = newTaskFor(task, result);
            executor.execute(new CategorizedQueueingFuture(f, task.getCategory()));
            return f;
        }

        public Future<V> submit(CategoryRunnable task) {
            return submit(task, null);
        }

        @Override
        public Future<V> submit(Callable<V> task) {
            throw new IllegalArgumentException("Must use a 'CategoryCallable'");
        }

        @Override
        public Future<V> submit(Runnable task, V result) {
            throw new IllegalArgumentException("Must use a 'CategoryRunnable'");
        }

        public Future<V> take() throws InterruptedException {
            return completionQueue.take();
        }

        public Future<V> poll() {
            return completionQueue.poll();
        }

        public Future<V> poll(long timeout, TimeUnit unit)
                throws InterruptedException {
            return completionQueue.poll(timeout, unit);
        }

        /**
         * FutureTask extension to enqueue upon completion + Category
         */
        public class CategorizedQueueingFuture extends FutureTask<Void> {

            private final Future<V> task;

            private int category;

            CategorizedQueueingFuture(RunnableFuture<V> task, int category) {
                super(task, null);
                this.task = task;
                this.category = category;
            }

            protected void done() {
                completionQueue.add(task);
            }

            public int getCategory() {
                return category;
            }
        }
    }

    public abstract class CategoryRunnable implements Runnable {

        private int category;

        public CategoryRunnable(int category) {
            this.category = category;
        }

        public int getCategory() {
            return category;
        }
    }

    public abstract class CategoryCallable<V> implements Callable<V> {

        private int category;

        public CategoryCallable(int category) {
            this.category = category;
        }

        public int getCategory() {
            return category;
        }
    }

    public class CategoryBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E> {

        private Map<Integer, LinkedBlockingQueue<E>> map = new HashMap<>();

        private AtomicInteger count = new AtomicInteger(0);

        private ReentrantLock lock = new ReentrantLock();

        private LinkedBlockingQueue<Integer> nextCategories = new LinkedBlockingQueue<>();

        @Override
        public boolean offer(E e) {
            CustomCompletionService.CategorizedQueueingFuture item = (CustomCompletionService.CategorizedQueueingFuture) e;
            lock.lock();
            try {
                int category = item.getCategory();
                if (!map.containsKey(category)) {
                    map.put(category, new LinkedBlockingQueue<E>());
                    nextCategories.offer(category);
                }
                boolean b = map.get(category).offer(e);
                if (b) {
                    count.incrementAndGet();
                }
                return b;
            } finally {
                lock.unlock();
            }
        }

        @Override
        public E poll() {
            return null;
        }

        @Override
        public E peek() {
            return null;
        }

        @Override
        public void put(E e) throws InterruptedException {

        }

        @Override
        public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
            return false;
        }

        @Override
        public E take() throws InterruptedException {
            lock.lockInterruptibly();
            try {
                Integer nextCategory = nextCategories.take();
                LinkedBlockingQueue<E> categoryElements = map.get(nextCategory);
                E e = categoryElements.take();
                count.decrementAndGet();
                if (categoryElements.isEmpty()) {
                    map.remove(nextCategory);
                } else {
                    nextCategories.offer(nextCategory);
                }
                return e;
            } finally {
                lock.unlock();
            }
        }

        @Override
        public boolean remove(Object o) {
            CustomCompletionService.CategorizedQueueingFuture item = (CustomCompletionService.CategorizedQueueingFuture) o;
            lock.lock();
            try {
                int category = item.getCategory();
                LinkedBlockingQueue<E> categoryElements = map.get(category);
                boolean b = categoryElements.remove(item);
                if (categoryElements.isEmpty()) {
                    map.remove(category);
                }
                if (b) {
                    count.decrementAndGet();
                }
                return b;
            } finally {
                lock.unlock();
            }
        }

        @Override
        public int drainTo(Collection<? super E> c) {
            return 0;
        }

        @Override
        public int drainTo(Collection<? super E> c, int maxElements) {
            return 0;
        }

        @Override
        public Iterator<E> iterator() {
            return null;
        }

        @Override
        public int size() {
            return count.get();
        }

        @Override
        public E poll(long timeout, TimeUnit unit) throws InterruptedException {
            // TODO
            return null;
        }

        @Override
        public int remainingCapacity() {
            return 0;
        }

    }
}

使用传统的LinkedBlockingQueue输出

2017-01-09 14:56:13,061 [pool-2-thread-1] DEBUG - thread 0 of request 0
2017-01-09 14:56:13,061 [pool-2-thread-4] DEBUG - thread 3 of request 0
2017-01-09 14:56:13,061 [pool-2-thread-2] DEBUG - thread 1 of request 0
2017-01-09 14:56:13,061 [pool-2-thread-3] DEBUG - thread 2 of request 0
2017-01-09 14:56:13,061 [pool-2-thread-5] DEBUG - thread 4 of request 0
2017-01-09 14:56:15,063 [pool-2-thread-2] DEBUG - thread 5 of request 0
2017-01-09 14:56:15,063 [pool-2-thread-1] DEBUG - thread 6 of request 0
2017-01-09 14:56:15,063 [pool-2-thread-4] DEBUG - thread 7 of request 0
2017-01-09 14:56:15,063 [pool-2-thread-3] DEBUG - thread 0 of request 1
2017-01-09 14:56:15,063 [pool-2-thread-5] DEBUG - thread 1 of request 1
2017-01-09 14:56:17,064 [pool-2-thread-2] DEBUG - thread 2 of request 1
2017-01-09 14:56:17,064 [pool-2-thread-4] DEBUG - thread 3 of request 1
2017-01-09 14:56:17,064 [pool-2-thread-1] DEBUG - thread 5 of request 1
2017-01-09 14:56:17,064 [pool-2-thread-3] DEBUG - thread 4 of request 1
2017-01-09 14:56:17,064 [pool-2-thread-5] DEBUG - thread 6 of request 1
2017-01-09 14:56:19,064 [pool-2-thread-4] DEBUG - thread 7 of request 1
2017-01-09 14:56:19,064 [pool-2-thread-1] DEBUG - thread 0 of request 2
2017-01-09 14:56:19,064 [pool-2-thread-3] DEBUG - thread 1 of request 2
2017-01-09 14:56:19,064 [pool-2-thread-5] DEBUG - thread 2 of request 2
2017-01-09 14:56:19,064 [pool-2-thread-2] DEBUG - thread 3 of request 2
2017-01-09 14:56:21,064 [pool-2-thread-4] DEBUG - thread 4 of request 2
2017-01-09 14:56:21,064 [pool-2-thread-3] DEBUG - thread 5 of request 2
2017-01-09 14:56:21,064 [pool-2-thread-5] DEBUG - thread 6 of request 2
2017-01-09 14:56:21,064 [pool-2-thread-2] DEBUG - thread 7 of request 2
2017-01-09 14:56:21,064 [pool-2-thread-1] DEBUG - thread 0 of request 3
2017-01-09 14:56:23,064 [pool-2-thread-4] DEBUG - thread 2 of request 3
2017-01-09 14:56:23,064 [pool-2-thread-3] DEBUG - thread 1 of request 3
2017-01-09 14:56:23,064 [pool-2-thread-2] DEBUG - thread 3 of request 3
2017-01-09 14:56:23,064 [pool-2-thread-1] DEBUG - thread 4 of request 3
2017-01-09 14:56:23,064 [pool-2-thread-5] DEBUG - thread 5 of request 3
2017-01-09 14:56:25,064 [pool-2-thread-2] DEBUG - thread 7 of request 3
2017-01-09 14:56:25,064 [pool-2-thread-1] DEBUG - thread 6 of request 3

使用我的自定义CategoryBlockingQueue输出

2017-01-09 14:54:54,765 [pool-2-thread-3] DEBUG - thread 2 of request 0
2017-01-09 14:54:54,765 [pool-2-thread-2] DEBUG - thread 1 of request 0
2017-01-09 14:54:54,765 [pool-2-thread-5] DEBUG - thread 4 of request 0
2017-01-09 14:54:54,765 [pool-2-thread-1] DEBUG - thread 0 of request 0
2017-01-09 14:54:54,765 [pool-2-thread-4] DEBUG - thread 3 of request 0
2017-01-09 14:54:56,767 [pool-2-thread-1] DEBUG - thread 0 of request 1
2017-01-09 14:54:56,767 [pool-2-thread-4] DEBUG - thread 0 of request 3
2017-01-09 14:54:56,767 [pool-2-thread-3] DEBUG - thread 5 of request 0
2017-01-09 14:54:56,767 [pool-2-thread-5] DEBUG - thread 0 of request 2
2017-01-09 14:54:56,767 [pool-2-thread-2] DEBUG - thread 6 of request 0
2017-01-09 14:54:58,767 [pool-2-thread-1] DEBUG - thread 1 of request 1
2017-01-09 14:54:58,767 [pool-2-thread-5] DEBUG - thread 1 of request 2
2017-01-09 14:54:58,767 [pool-2-thread-2] DEBUG - thread 7 of request 0
2017-01-09 14:54:58,767 [pool-2-thread-4] DEBUG - thread 1 of request 3
2017-01-09 14:54:58,767 [pool-2-thread-3] DEBUG - thread 2 of request 1
2017-01-09 14:55:00,767 [pool-2-thread-1] DEBUG - thread 2 of request 2
2017-01-09 14:55:00,767 [pool-2-thread-5] DEBUG - thread 2 of request 3
2017-01-09 14:55:00,767 [pool-2-thread-2] DEBUG - thread 3 of request 1
2017-01-09 14:55:00,767 [pool-2-thread-4] DEBUG - thread 3 of request 2
2017-01-09 14:55:00,767 [pool-2-thread-3] DEBUG - thread 3 of request 3
2017-01-09 14:55:02,767 [pool-2-thread-5] DEBUG - thread 4 of request 1
2017-01-09 14:55:02,767 [pool-2-thread-3] DEBUG - thread 4 of request 2
2017-01-09 14:55:02,767 [pool-2-thread-2] DEBUG - thread 4 of request 3
2017-01-09 14:55:02,767 [pool-2-thread-1] DEBUG - thread 5 of request 1
2017-01-09 14:55:02,767 [pool-2-thread-4] DEBUG - thread 5 of request 2
2017-01-09 14:55:04,767 [pool-2-thread-2] DEBUG - thread 5 of request 3
2017-01-09 14:55:04,767 [pool-2-thread-1] DEBUG - thread 6 of request 1
2017-01-09 14:55:04,767 [pool-2-thread-5] DEBUG - thread 6 of request 2
2017-01-09 14:55:04,767 [pool-2-thread-3] DEBUG - thread 6 of request 3
2017-01-09 14:55:04,768 [pool-2-thread-4] DEBUG - thread 7 of request 1
2017-01-09 14:55:06,768 [pool-2-thread-2] DEBUG - thread 7 of request 3
2017-01-09 14:55:06,768 [pool-2-thread-1] DEBUG - thread 7 of request 2

3 个答案:

答案 0 :(得分:0)

我已经离开了链接,它可能对你自己的公平锁定实现有用。

http://tutorials.jenkov.com/java-concurrency/starvation-and-fairness.html

答案 1 :(得分:0)

保持简单。

  1. 为较小和较长的任务提供两个专用线程池。
  2. 最好使用Executors.html#newFixedThreadPoolExecutors.html#newWorkStealingPool
  3. 工作窃取线程池有效地使用可用的CPU核心。

    有关详细信息,请查看以下相关的SE问题:

    Java: How to scale threads according to cpu cores?

答案 2 :(得分:0)

最后,我采取的措施是以“公平”,“平衡”的方式为每个并行请求提供池的线程。这有效。如果出现问题或有更好的方法,请告诉我。

总结一下,我创建了一个供池使用的BlockingQueue。此队列将请求的任务存储在Map中,Map根据与其相关的请求对它们进行分类。然后,池调用以获取要执行的新任务的take或offer方法每次都会从新请求中提供任务。

我需要调整CompletionService以处理Runnable和Callable,并附加一个字段作为请求的ID。

public class TestThreadPoolExecutorWithTurningQueue {

    private final static Logger logger = LogManager.getLogger();

    private static ThreadPoolExecutor executorService;

    int nbRequest = 4;

    int nbThreadPerRequest = 8;

    int threadPoolSize = 5;

    private void init() {
        executorService = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 10L, TimeUnit.SECONDS,
                new CategoryBlockingQueue<Runnable>()// my custom blocking queue storing waiting tasks per request
                //new LinkedBlockingQueue<Runnable>()
        );
        executorService.allowCoreThreadTimeOut(true);
    }

    @Test
    public void test() throws Exception {
        init();
        ExecutorService tomcat = Executors.newFixedThreadPool(nbRequest);
        for (int i = 0; i < nbRequest; i++) {
            Thread.sleep(10);
            final int finalI = i;
            tomcat.execute(new Runnable() {
                @Override
                public void run() {
                    request(finalI);
                }
            });
        }

        for (int i = 0; i < 100; i++) {
            Thread.sleep(1000);
            logger.debug("TPE = " + executorService);
        }

        tomcat.shutdown();
        tomcat.awaitTermination(1, TimeUnit.DAYS);
    }

    public void request(final int requestId) {
        CustomCompletionService<Object> completionService = new CustomCompletionService<>(executorService);
        for (int j = 0; j < nbThreadPerRequest; j++) {
            final int finalJ = j;
            completionService.submit(new CategoryRunnable(requestId) {
                @Override
                public void go() throws Exception {
                    logger.debug("thread " + finalJ + " of request " + requestId + "   " + executorService);
                    Thread.sleep(2000);// here should come the useful things to be done
                }
            });
        }
        completionService.awaitCompletion();
    }

    public class CustomCompletionService<V> implements CompletionService<V> {

        private final Executor executor;

        private final BlockingQueue<Future<V>> completionQueue;

        private List<RunnableFuture<V>> submittedTasks = new ArrayList<>();

        public CustomCompletionService(Executor executor) {
            if (executor == null)
                throw new NullPointerException();
            this.executor = executor;
            this.completionQueue = new LinkedBlockingQueue<>();
        }

        public void awaitCompletion() {
            for (int i = 0; i < submittedTasks.size(); i++) {
                try {
                    take().get();
                } catch (Exception e) {
                    // Cancel the remaining tasks
                    for (RunnableFuture<V> f : submittedTasks) {
                        f.cancel(true);
                    }

                    // Get the underlying exception
                    Exception toThrow = e;
                    if (e instanceof ExecutionException) {
                        ExecutionException ex = (ExecutionException) e;
                        toThrow = (Exception) ex.getCause();
                    }
                    throw new RuntimeException(toThrow);
                }
            }
        }

        private RunnableFuture<V> newTaskFor(Callable<V> task) {
            return new FutureTask<V>(task);
        }

        private RunnableFuture<V> newTaskFor(Runnable task, V result) {
            return new FutureTask<V>(task, result);
        }

        public Future<V> submit(CategoryCallable<V> task) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<V> f = newTaskFor(task);
            executor.execute(new CategorizedQueueingFuture(f, task.getCategory()));
            submittedTasks.add(f);
            return f;
        }

        public Future<V> submit(CategoryRunnable task, V result) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<V> f = newTaskFor(task, result);
            executor.execute(new CategorizedQueueingFuture(f, task.getCategory()));
            submittedTasks.add(f);
            return f;
        }

        public Future<V> submit(CategoryRunnable task) {
            return submit(task, null);
        }

        @Override
        public Future<V> submit(Callable<V> task) {
            throw new IllegalArgumentException("Must use a 'CategoryCallable'");
        }

        @Override
        public Future<V> submit(Runnable task, V result) {
            throw new IllegalArgumentException("Must use a 'CategoryRunnable'");
        }

        public Future<V> take() throws InterruptedException {
            return completionQueue.take();
        }

        public Future<V> poll() {
            return completionQueue.poll();
        }

        public Future<V> poll(long timeout, TimeUnit unit)
                throws InterruptedException {
            return completionQueue.poll(timeout, unit);
        }

        /**
         * FutureTask extension to enqueue upon completion + Category
         */
        public class CategorizedQueueingFuture extends FutureTask<Void> {

            private final Future<V> task;

            private int category;

            CategorizedQueueingFuture(RunnableFuture<V> task, int category) {
                super(task, null);
                this.task = task;
                this.category = category;
            }

            protected void done() {
                completionQueue.add(task);
            }

            public int getCategory() {
                return category;
            }
        }
    }

    public abstract class CategoryRunnable implements Runnable {

        private int category;

        public CategoryRunnable(int category) {
            this.category = category;
        }

        public int getCategory() {
            return category;
        }

        void go() throws Exception {
            // To be implemented. Do nothing by default.
            logger.warn("Implement go method !");
        }

        @Override
        public void run() {
            try {
                go();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public abstract class CategoryCallable<V> implements Callable<V> {

        private int category;

        public CategoryCallable(int category) {
            this.category = category;
        }

        public int getCategory() {
            return category;
        }
    }

    public class CategoryBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E> {

        /**
         * Lock held by take, poll, etc
         */
        private final ReentrantLock takeLock = new ReentrantLock();

        /**
         * Wait queue for waiting takes
         */
        private final Condition notEmpty = takeLock.newCondition();

        /**
         * Lock held by put, offer, etc
         */
        private final ReentrantLock putLock = new ReentrantLock();

        private Map<Integer, LinkedBlockingQueue<E>> map = new ConcurrentHashMap<>();

        private AtomicInteger count = new AtomicInteger(0);

        private LinkedBlockingQueue<Integer> nextCategories = new LinkedBlockingQueue<>();

        @Override
        public boolean offer(E e) {
            CustomCompletionService.CategorizedQueueingFuture item = (CustomCompletionService.CategorizedQueueingFuture) e;
            putLock.lock();
            try {
                int category = item.getCategory();
                if (!map.containsKey(category)) {
                    map.put(category, new LinkedBlockingQueue<E>());
                    if (!nextCategories.offer(category)) return false;
                }
                if (!map.get(category).offer(e)) return false;
                int c = count.getAndIncrement();
                if (c == 0) signalNotEmpty();// if we passed from 0 element (empty queue) to 1 element, signal potentially waiting threads on take
                return true;
            } finally {
                putLock.unlock();
            }
        }

        private void signalNotEmpty() {
            takeLock.lock();
            try {
                notEmpty.signal();
            } finally {
                takeLock.unlock();
            }
        }

        @Override
        public E take() throws InterruptedException {
            takeLock.lockInterruptibly();
            try {
                while (count.get() == 0) {
                    notEmpty.await();
                }
                E e = dequeue();
                int c = count.decrementAndGet();
                if (c > 0) notEmpty.signal();
                return e;
            } finally {
                takeLock.unlock();
            }
        }

        private E dequeue() throws InterruptedException {
            Integer nextCategory = nextCategories.take();
            LinkedBlockingQueue<E> categoryElements = map.get(nextCategory);
            E e = categoryElements.take();
            if (categoryElements.isEmpty()) {
                map.remove(nextCategory);
            } else {
                nextCategories.offer(nextCategory);
            }
            return e;
        }

        @Override
        public E poll(long timeout, TimeUnit unit) throws InterruptedException {
            E x = null;
            long nanos = unit.toNanos(timeout);
            takeLock.lockInterruptibly();
            try {
                while (count.get() == 0) {
                    if (nanos <= 0) return null;
                    nanos = notEmpty.awaitNanos(nanos);
                }
                x = dequeue();
                int c = count.decrementAndGet();
                if (c > 0) notEmpty.signal();
            } finally {
                takeLock.unlock();
            }
            return x;
        }

        @Override
        public boolean remove(Object o) {
            if (o == null) return false;
            CustomCompletionService.CategorizedQueueingFuture item = (CustomCompletionService.CategorizedQueueingFuture) o;
            putLock.lock();
            takeLock.lock();
            try {
                int category = item.getCategory();
                LinkedBlockingQueue<E> categoryElements = map.get(category);
                boolean b = categoryElements.remove(item);
                if (categoryElements.isEmpty()) {
                    map.remove(category);
                }
                if (b) {
                    count.decrementAndGet();
                }
                return b;
            } finally {
                takeLock.unlock();
                putLock.unlock();
            }
        }

        @Override
        public E poll() {
            return null;
        }

        @Override
        public E peek() {
            return null;
        }

        @Override
        public void put(E e) throws InterruptedException {

        }

        @Override
        public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
            return false;
        }

        @Override
        public int drainTo(Collection<? super E> c) {
            return 0;
        }

        @Override
        public int drainTo(Collection<? super E> c, int maxElements) {
            return 0;
        }

        @Override
        public Iterator<E> iterator() {
            return null;
        }

        @Override
        public int size() {
            return count.get();
        }

        @Override
        public int remainingCapacity() {
            return 0;
        }

    }

}