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 }