View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
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  package net.sf.ehcache.concurrent;
17  
18  import java.util.Arrays;
19  import java.util.Collections;
20  import java.util.List;
21  import java.util.concurrent.locks.ReadWriteLock;
22  
23  import net.sf.ehcache.CacheException;
24  
25  /***
26   * Provides a number of Sync which allow fine-grained concurrency. Rather than locking a cache or a store,
27   * the individual elements or constituent objects can be locked. This dramatically increases
28   * the possible concurrency.
29   * <p/>
30   * The more stripes, the higher the concurrency. To be threadsafe, the instance of CacheLockProvider needs to be
31   * maintained for the entire life of the cache or store, so there is some added memory use.
32   * <p/>
33   * Though a new class, this code has been refactored from <code>BlockingCache</code>, where it has been in use
34   * in highly concurrent production environments for years.
35   * <p/>
36   * Based on the lock striping concept from Brian Goetz. See Java Concurrency in Practice 11.4.3
37   * @author Alex Snaps
38   */
39  public class StripedReadWriteLockSync implements CacheLockProvider {
40  
41      /***
42       * The default number of locks to use. Must be a power of 2.
43       * <p/>
44       * The choice of 2048 enables 2048 concurrent operations per cache or cache store, which should be enough for most
45       * uses.
46       */
47      public static final int DEFAULT_NUMBER_OF_MUTEXES = 2048;
48  
49      private final ReadWriteLockSync[] mutexes;
50      private final List<ReadWriteLockSync> mutexesAsList;
51      /***
52       * Constructs a striped mutex with the default 2048 stripes.
53       */
54      public StripedReadWriteLockSync() {
55          this(DEFAULT_NUMBER_OF_MUTEXES);
56      }
57  
58      /***
59       * Constructs a striped mutex with the default 2048 stripes.
60       * <p/>
61       * The number of stripes determines the number of concurrent operations per cache or cache store.
62       * @param numberOfStripes - must be a factor of two
63       */
64      public StripedReadWriteLockSync(int numberOfStripes) {
65          if ((numberOfStripes & (numberOfStripes - 1)) != 0) {
66              throw new CacheException("Cannot create a CacheLockProvider with a non power-of-two number of stripes");
67          }
68          if (numberOfStripes == 0) {
69              throw new CacheException("A zero size CacheLockProvider does not have useful semantics.");
70          }
71  
72          mutexes = new ReadWriteLockSync[numberOfStripes];
73  
74          for (int i = 0; i < mutexes.length; i++) {
75              mutexes[i] = new ReadWriteLockSync();
76          }
77          mutexesAsList = Collections.unmodifiableList(Arrays.asList(mutexes));
78      }
79  
80      /***
81       * Gets the Sync Stripe to use for a given key.
82       * <p/>
83       * This lookup must always return the same Sync for a given key.
84       * <p/>
85       * @param key the key
86       * @return one of a limited number of Sync's.
87       */
88      public ReadWriteLockSync getSyncForKey(final Object key) {
89          int lockNumber = ConcurrencyUtil.selectLock(key, mutexes.length);
90          return mutexes[lockNumber];
91      }
92  
93      /***
94       * Gets the RWL Stripe to use for a given key.
95       * <p/>
96       * This lookup must always return the same RWL for a given key.
97       * <p/>
98       * @param key the key
99       * @return one of a limited number of RWLs.
100      */
101     public ReadWriteLock getLockForKey(final Object key) {
102         int lockNumber = ConcurrencyUtil.selectLock(key, mutexes.length);
103         return mutexes[lockNumber].getReadWriteLock();
104     }
105     
106     /***
107      * Returns all internal syncs
108      * @return all internal syncs
109      */
110     public List<ReadWriteLockSync> getAllSyncs() {
111         return mutexesAsList;
112     }
113 }