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<String> locker = new Locker<String>();
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 }