View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.util;
20  
21  
22  import java.util.Arrays;
23  import java.util.LinkedHashMap;
24  import java.util.Map;
25  import java.util.Set;
26  import java.util.concurrent.locks.Lock;
27  import java.util.concurrent.locks.ReentrantLock;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  
33  /**
34   * A utility class to manage a set of locks. Each lock is identified by a String which serves
35   * as a key. Typical usage is: <pre>
36   * class Example {
37   *   private final static KeyLocker&lt;String&gt; locker = new Locker&lt;String&gt;();
38   *   public void foo(String s){
39   *     Lock lock = locker.acquireLock(s);
40   *     try {
41   *       // whatever
42   *     }finally{
43   *       lock.unlock();
44   *     }
45   *   }
46   * }
47   * </pre>
48   */
49  @InterfaceAudience.Private
50  public class KeyLocker<K> {
51    private static final Log LOG = LogFactory.getLog(KeyLocker.class);
52  
53    // The number of lock we want to easily support. It's not a maximum.
54    private static final int NB_CONCURRENT_LOCKS = 1000;
55  
56    private final WeakObjectPool<K, ReentrantLock> lockPool =
57        new WeakObjectPool<K, ReentrantLock>(
58            new WeakObjectPool.ObjectFactory<K, ReentrantLock>() {
59              @Override
60              public ReentrantLock createObject(K key) {
61                return new ReentrantLock();
62              }
63            },
64            NB_CONCURRENT_LOCKS);
65  
66    /**
67     * Return a lock for the given key. The lock is already locked.
68     *
69     * @param key
70     */
71    public ReentrantLock acquireLock(K key) {
72      if (key == null) throw new IllegalArgumentException("key must not be null");
73  
74      lockPool.purge();
75      ReentrantLock lock = lockPool.get(key);
76  
77      lock.lock();
78      return lock;
79    }
80  
81    /**
82     * Acquire locks for a set of keys. The keys will be
83     * sorted internally to avoid possible deadlock.
84     *
85     * @throw ClassCastException if the given {@code keys}
86     *    contains elements that are not mutually comparable
87     */
88    public Map<K, Lock> acquireLocks(Set<? extends K> keys) {
89      Object[] keyArray = keys.toArray();
90      Arrays.sort(keyArray);
91  
92      lockPool.purge();
93      Map<K, Lock> locks = new LinkedHashMap<K, Lock>(keyArray.length);
94      for (Object o : keyArray) {
95        @SuppressWarnings("unchecked")
96        K key = (K)o;
97        ReentrantLock lock = lockPool.get(key);
98        locks.put(key, lock);
99      }
100 
101     for (Lock lock : locks.values()) {
102       lock.lock();
103     }
104     return locks;
105   }
106 }