View Javadoc

1   /**
2    * Copyright 2008 Atlassian Pty Ltd 
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package com.atlassian.util.concurrent;
18  
19  import java.util.concurrent.Callable;
20  import java.util.concurrent.locks.Lock;
21  
22  /**
23   * Static factory for producing {@link Manager} instances.
24   */
25  public class LockManagers {
26  
27      private static final class Defaults {
28          private static final int CAPACITY = 128;
29      }
30  
31      /**
32       * Convenience method that simply calls {@link LockManagers#weakLockManager(Function, int)} with
33       * a default capacity of 128.
34       * 
35       * @see LockManagers#weakLockManager(Function, int)
36       */
37      public static <T, D> LockManager<T> weakLockManager(final Function<T, D> stripeFunction) {
38          return weakLockManager(stripeFunction, Defaults.CAPACITY);
39      }
40  
41      /**
42       * The particular {@link Lock} is resolved using a {@link Function} that resolves to a
43       * descriptor used to look up a Lock instance. This allows for a finite set of locks to be used
44       * even if the set of T is essentially unbounded.
45       * <p>
46       * For instance:
47       * 
48       * <pre>
49       * LockManager&lt;Identifiable, Integer&gt; manager = LockManagers.weakLockManager(new Function&lt;Identifiable, Integer&gt;() {
50       *     Integer get(Identifiable thing) {
51       *         return thing.getId() % 16;
52       *     }
53       * };
54       * </pre>
55       * 
56       * uses only 16 possible locks as the function returns the modulo 16 of the thing's id.
57       * 
58       * @param <T> the type of the thing used to look up locks
59       * @param <D> the type used to map lock instances
60       * @param stripeFunction to convert Ts to Ds.
61       * @return a new {@link LockManager} instance that stores created {@link Lock} instances with
62       *         weak references.
63       */
64      public static <T, D> LockManager<T> weakLockManager(final Function<T, D> stripeFunction, final int capacity) {
65          return new Manager<T, D>(new WeakLockMap<D>(capacity), stripeFunction);
66      }
67  
68      private LockManagers() {
69          throw new AssertionError("cannot instantiate!");
70      }
71  
72      /**
73       * Default implementation of {@link LockManager}
74       * 
75       * @param <T> the input type
76       * @param <D> the type used for the internal lock resolution.
77       */
78      static class Manager<T, D> implements LockManager<T> {
79          private final Function<D, Lock> lockResolver;
80          private final Function<T, D> stripeFunction;
81  
82          Manager(final Function<D, Lock> lockResolver, final Function<T, D> stripeFunction) {
83              this.lockResolver = lockResolver;
84              this.stripeFunction = stripeFunction;
85          }
86  
87          public <R> R withLock(final T descriptor, final Callable<R> callable) throws Exception {
88              final Lock lock = lockResolver.get(stripeFunction.get(descriptor));
89              lock.lock();
90              try {
91                  return callable.call();
92              }
93              finally {
94                  lock.unlock();
95              }
96          }
97  
98          public void withLock(final T descriptor, final Runnable runnable) {
99              final Lock lock = lockResolver.get(stripeFunction.get(descriptor));
100             lock.lock();
101             try {
102                 runnable.run();
103             }
104             finally {
105                 lock.unlock();
106             }
107         }
108     }
109 }