001
014
015 package com.liferay.portal.kernel.concurrent;
016
017 import com.liferay.portal.kernel.util.StringBundler;
018
019 import java.util.concurrent.atomic.AtomicInteger;
020 import java.util.concurrent.atomic.AtomicLong;
021 import java.util.concurrent.locks.Lock;
022 import java.util.concurrent.locks.ReentrantReadWriteLock;
023
024
027 public class ConcurrentLRUCache<K, V> {
028
029 public ConcurrentLRUCache(int maxSize) {
030 _maxSize = maxSize;
031
032 _readLock = _readWriteLock.readLock();
033 _writeLock = _readWriteLock.writeLock();
034
035 _headEntry._nextEntry = _lastEntry;
036 _lastEntry._previousEntry = _headEntry;
037 }
038
039 public long evictCount() {
040 return _evictCount.get();
041 }
042
043 public V get(K key) {
044 Entry<K, V> matchEntry = null;
045
046 boolean requiresMove = false;
047
048 _readLock.lock();
049
050 try {
051 matchEntry = _lastEntry._previousEntry;
052
053 while (matchEntry != _headEntry) {
054 if (matchEntry._key.equals(key)) {
055 if (matchEntry._nextEntry != _lastEntry) {
056 requiresMove = true;
057 }
058
059 _hitCount.getAndIncrement();
060
061 return matchEntry._value;
062 }
063
064 matchEntry = matchEntry._previousEntry;
065 }
066 }
067 finally {
068 _readLock.unlock();
069
070 if (requiresMove) {
071 _writeLock.lock();
072
073 try {
074 if (matchEntry._key != null) {
075 _detachEntry(matchEntry);
076
077 _insertEntryBefore(_lastEntry, matchEntry);
078 }
079 }
080 finally {
081 _writeLock.unlock();
082 }
083 }
084 }
085
086 _missCount.getAndIncrement();
087
088 return null;
089 }
090
091 public long hitCount() {
092 return _hitCount.get();
093 }
094
095 public int maxSize() {
096 return _maxSize;
097 }
098
099 public long missCount() {
100 return _missCount.get();
101 }
102
103 public void put(K key, V value) {
104 if (key == null) {
105 throw new NullPointerException("Key is null");
106 }
107
108 _putCount.getAndIncrement();
109 _writeLock.lock();
110
111 try {
112 Entry<K, V> currentEntry = _lastEntry._previousEntry;
113
114 while (currentEntry != _headEntry) {
115 if (currentEntry._key.equals(key)) {
116 currentEntry._value = value;
117
118 if (currentEntry._nextEntry != _lastEntry) {
119 _detachEntry(currentEntry);
120 _insertEntryBefore(_lastEntry, currentEntry);
121 }
122
123 return;
124 }
125
126 currentEntry = currentEntry._previousEntry;
127 }
128
129 while (_size.get() >= _maxSize) {
130 _removeHeadEntry();
131 }
132
133 _insertEntryBefore(_lastEntry, new Entry<K, V>(key, value));
134
135 _size.getAndIncrement();
136 }
137 finally {
138 _writeLock.unlock();
139 }
140 }
141
142 public long putCount() {
143 return _putCount.get();
144 }
145
146 public int size() {
147 return _size.get();
148 }
149
150 public String toString() {
151 StringBundler sb = new StringBundler();
152
153 sb.append("{evictCount=");
154 sb.append(_evictCount);
155 sb.append(", hitCount=");
156 sb.append(_hitCount);
157 sb.append(", maxSize=");
158 sb.append(_maxSize);
159 sb.append(", missCount=");
160 sb.append(_missCount);
161 sb.append(", putCount=");
162 sb.append(_putCount);
163 sb.append(", size=");
164 sb.append(_size);
165 sb.append("}");
166
167 return sb.toString();
168 }
169
170 private void _detachEntry(Entry<K, V> entry) {
171 entry._previousEntry._nextEntry = entry._nextEntry;
172 entry._nextEntry._previousEntry = entry._previousEntry;
173 entry._nextEntry = entry._previousEntry = null;
174 }
175
176 private void _insertEntryBefore(
177 Entry<K, V> referenceEntry, Entry<K, V> insertEntry) {
178
179 insertEntry._previousEntry = referenceEntry._previousEntry;
180 insertEntry._nextEntry = referenceEntry;
181
182 referenceEntry._previousEntry._nextEntry = insertEntry;
183 referenceEntry._previousEntry = insertEntry;
184 }
185
186 private void _removeHeadEntry() {
187 Entry<K, V> entry = _headEntry._nextEntry;
188
189 _detachEntry(entry);
190
191 entry._key = null;
192 entry._value = null;
193
194 _size.getAndDecrement();
195 _evictCount.getAndIncrement();
196 }
197
198 private final AtomicLong _evictCount = new AtomicLong(0);
199 private final Entry<K, V> _headEntry = new Entry<K, V>(null, null);
200 private final AtomicLong _hitCount = new AtomicLong(0);
201 private final Entry<K, V> _lastEntry = new Entry<K, V>(null, null);
202 private final int _maxSize;
203 private final AtomicLong _missCount = new AtomicLong(0);
204 private final AtomicLong _putCount = new AtomicLong(0);
205 private final Lock _readLock;
206 private final ReentrantReadWriteLock _readWriteLock =
207 new ReentrantReadWriteLock();
208 private final AtomicInteger _size = new AtomicInteger(0);
209 private final Lock _writeLock;
210
211 private static class Entry<K, V> {
212
213 public Entry(K key, V value) {
214 _key = key;
215 _value = value;
216 }
217
218 private K _key;
219 private Entry<K, V> _nextEntry;
220 private Entry<K, V> _previousEntry;
221 private V _value;
222
223 }
224
225 }