View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.util;
20  
21  import java.util.concurrent.BlockingQueue;
22  import java.util.concurrent.PriorityBlockingQueue;
23  import java.util.concurrent.TimeUnit;
24  import java.util.concurrent.locks.Condition;
25  import java.util.concurrent.locks.Lock;
26  import java.util.concurrent.locks.ReentrantLock;
27  
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  
30  /**
31   * This queue allows a ThreadPoolExecutor to steal jobs from another ThreadPoolExecutor.
32   * This queue also acts as the factory for creating the PriorityBlockingQueue to be used in the
33   * steal-from ThreadPoolExecutor. The behavior of this queue is the same as a normal
34   * PriorityBlockingQueue except the take/poll(long,TimeUnit) methods would also check whether there
35   * are jobs in the steal-from queue if this q ueue is empty.
36   *
37   * Note the workers in ThreadPoolExecutor must be pre-started so that they can steal job from the
38   * other queue, otherwise the worker will only be started after there are jobs submitted to main
39   * queue.
40   */
41  @InterfaceAudience.Private
42  public class StealJobQueue<T> extends PriorityBlockingQueue<T> {
43  
44    private BlockingQueue<T> stealFromQueue;
45  
46    private final Lock lock = new ReentrantLock();
47    private final Condition notEmpty = lock.newCondition();
48  
49    public StealJobQueue() {
50      this.stealFromQueue = new PriorityBlockingQueue<T>() {
51  
52        @Override
53        public boolean offer(T t) {
54          lock.lock();
55          try {
56            notEmpty.signal();
57            return super.offer(t);
58          } finally {
59            lock.unlock();
60          }
61        }
62      };
63    }
64  
65    public StealJobQueue(int initCapacity, int stealFromQueueInitCapacity) {
66      super(initCapacity);
67      this.stealFromQueue = new PriorityBlockingQueue<T>(stealFromQueueInitCapacity) {
68  
69        @Override
70        public boolean offer(T t) {
71          lock.lock();
72          try {
73            notEmpty.signal();
74            return super.offer(t);
75          } finally {
76            lock.unlock();
77          }
78        }
79      };
80    }
81  
82    /**
83     * Get a queue whose job might be stolen by the consumer of this original queue
84     * @return the queue whose job could be stolen
85     */
86    public BlockingQueue<T> getStealFromQueue() {
87      return stealFromQueue;
88    }
89  
90    @Override
91    public boolean offer(T t) {
92      lock.lock();
93      try {
94        notEmpty.signal();
95        return super.offer(t);
96      } finally {
97        lock.unlock();
98      }
99    }
100 
101 
102   @Override
103   public T take() throws InterruptedException {
104     lock.lockInterruptibly();
105     try {
106       while (true) {
107         T retVal = this.poll();
108         if (retVal == null) {
109           retVal = stealFromQueue.poll();
110         }
111         if (retVal == null) {
112           notEmpty.await();
113         } else {
114           return retVal;
115         }
116       }
117     } finally {
118       lock.unlock();
119     }
120   }
121 
122   @Override
123   public T poll(long timeout, TimeUnit unit) throws InterruptedException {
124     long nanos = unit.toNanos(timeout);
125     lock.lockInterruptibly();
126     try {
127       while (true) {
128         T retVal = this.poll();
129         if (retVal == null) {
130           retVal = stealFromQueue.poll();
131         }
132         if (retVal == null) {
133           if (nanos <= 0) {
134             return null;
135           }
136           nanos = notEmpty.awaitNanos(nanos);
137         } else {
138           return retVal;
139         }
140       }
141     } finally {
142       lock.unlock();
143     }
144   }
145 }
146