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 static com.atlassian.util.concurrent.Assertions.notNull;
20  
21  import java.lang.ref.ReferenceQueue;
22  import java.lang.ref.WeakReference;
23  import java.util.concurrent.ConcurrentHashMap;
24  import java.util.concurrent.ConcurrentMap;
25  import java.util.concurrent.locks.Lock;
26  import java.util.concurrent.locks.ReentrantLock;
27  
28  /**
29   * {@link WeakLockMap} holds a {@link Lock} per Descriptor.
30   * <p>
31   * Each {@link Lock} is {@link WeakReference weakly referenced} internally.
32   * 
33   * @param <D> comparable descriptor, should have a good hash function.
34   */
35  class WeakLockMap<D> implements Function<D, Lock> {
36      private final ConcurrentMap<D, WeakReference<Lock>> locks;
37      private final ReferenceQueue<Lock> queue;
38  
39      /**
40       * Construct a new {@link WeakLockMap} instance.
41       * 
42       * @param initialCapacity how large the internal map should be initially.
43       * @throws IllegalArgumentException if the initial capacity of elements is negative.
44       */
45      WeakLockMap(final int initialCapacity) {
46          locks = new ConcurrentHashMap<D, WeakReference<Lock>>(initialCapacity);
47          queue = new ReferenceQueue<Lock>();
48      }
49  
50      /**
51       * Get a Lock for the supplied Descriptor.
52       * 
53       * @param descriptors
54       * @return descriptor lock
55       */
56      public Lock get(final D descriptor) {
57          expungeStaleEntries();
58  
59          while (true) {
60              final WeakReference<Lock> reference = locks.get(descriptor);
61              if (reference != null) {
62                  final Lock lock = reference.get();
63                  if (lock != null) {
64                      return lock;
65                  }
66                  locks.remove(descriptor, reference);
67              }
68              locks.putIfAbsent(descriptor, new LockReference<D>(descriptor, queue));
69          }
70      }
71  
72      // expunge descriptors whose lock reference has been collected
73      @SuppressWarnings("unchecked") private void expungeStaleEntries() {
74          LockReference<D> ref;
75          while ((ref = (LockReference<D>) queue.poll()) != null) {
76              locks.remove(ref.getDescriptor(), ref);
77          }
78      }
79  
80      /**
81       * A weak reference that maintains a reference to the descriptor so that it can be removed from
82       * the map when garbage collected
83       */
84      private static class LockReference<D> extends WeakReference<Lock> {
85          private final D descriptor;
86  
87          public LockReference(final D descriptor, final ReferenceQueue<? super Lock> q) {
88              super(new ReentrantLock(), q);
89              this.descriptor = notNull("descriptor", descriptor);
90          }
91  
92          public D getDescriptor() {
93              return descriptor;
94          }
95      }
96  }