diff --git a/src/main/java/org/beetl/core/GroupTemplate.java b/src/main/java/org/beetl/core/GroupTemplate.java index 5dfd1d90fc059b922f327f44160f2401063ace5e..463bf4989c4017cc3fa81dacd90b806c452634af 100644 --- a/src/main/java/org/beetl/core/GroupTemplate.java +++ b/src/main/java/org/beetl/core/GroupTemplate.java @@ -39,6 +39,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.locks.ReentrantLock; import org.beetl.core.cache.Cache; import org.beetl.core.cache.ProgramCacheFactory; @@ -98,6 +99,8 @@ public class GroupTemplate { // 用于解析html tag得属性,转化为符合js变量名字 AttributeNameConvert htmlTagAttrNameConvert = null; + KeyLock keyLock = new KeyLock<>(); + /** * 使用默认的配置和默认的模板资源加载器{@link ClasspathResourceLoader}, @@ -407,21 +410,30 @@ public class GroupTemplate { private Script loadScriptTemplate(String key, ResourceLoader loader) { Program program = (Program) this.programCache.get(key); if (program == null) { - synchronized (key) { + ReentrantLock lock = keyLock.lockFor(key); + lock.lock(); + try { + program = (Program) this.programCache.get(key); if (program == null) { Resource resource = loader.getResource(key); program = this.loadScript(resource); this.programCache.set(key, program); return new Script(this, program, this.conf); } + } finally { + lock.unlock(); } } if (resourceLoader.isModified(program.res)) { - synchronized (key) { + ReentrantLock lock = keyLock.lockFor(key); + lock.lock(); + try { Resource resource = loader.getResource(key); program = this.loadScript(resource); this.programCache.set(key, program); + } finally { + lock.unlock(); } } @@ -495,22 +507,31 @@ public class GroupTemplate { // key = key.intern(); Program program = (Program) this.programCache.get(key); if (program == null) { - synchronized (key) { + ReentrantLock lock = keyLock.lockFor(key); + lock.lock(); + try { + program = (Program) this.programCache.get(key); if (program == null) { Resource resource = loader.getResource(key); program = this.loadTemplate(resource); this.programCache.set(key, program); return buffers==null?new Template(this, program, this.conf):new Template(this, program, this.conf,buffers); } + } finally { + lock.unlock(); } } if (resourceLoader.isModified(program.res)) { - synchronized (key) { + ReentrantLock lock = keyLock.lockFor(key); + lock.lock(); + try { Resource resource = loader.getResource(key); program = this.loadTemplate(resource); this.programCache.set(key, program); - } + } finally { + lock.unlock(); + } } return buffers==null?new Template(this, program, this.conf):new Template(this, program, this.conf,buffers); diff --git a/src/main/java/org/beetl/core/KeyLock.java b/src/main/java/org/beetl/core/KeyLock.java new file mode 100644 index 0000000000000000000000000000000000000000..2645dad5057a9f6a0027a6770135ff1354b3900e --- /dev/null +++ b/src/main/java/org/beetl/core/KeyLock.java @@ -0,0 +1,128 @@ +package org.beetl.core; + +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * 按指定Key,生成锁,如果锁不再引用,由系统GC自动回收,也可手动回收(调用clear方法)不再使用的锁 + * + * @param + * @author butnet + */ +public class KeyLock { + private final WeakHashMap> keys = new WeakHashMap>(); + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock(); + private final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock(); + + /** + * 获取指定Key的锁对象,如果不存在,创建一个 + * + * @param key + * @return + */ + public ReentrantLock lockFor(Key key) { + readLock.lock(); + try { + WeakReference reference = keys.get(key); + if (reference != null) { + ReentrantLock lock = reference.get(); + if (lock != null) { + return lock; + } + } + } finally { + readLock.unlock(); + } + + return createLock(key); + } + + /** + * 判断锁是否存在 + * + * @param key + * @return + */ + public boolean hasLock(Key key) { + readLock.lock(); + try { + WeakReference reference = keys.get(key); + if (reference == null) { + return false; + } + return reference.get() != null; + } finally { + readLock.unlock(); + } + } + + /** + * 手动清理 + * + * @param key + * @return 清理成功返回true, 失败返回false, 如果key对应的锁存在引用,则返回false + */ + public boolean remove(Key key) { + writeLock.lock(); + try { + WeakReference reference = keys.get(key); + if (reference == null) { + return false; + } + if (reference.get() == null || reference.isEnqueued()) { + keys.remove(key); + return true; + } + } finally { + writeLock.unlock(); + } + return false; + } + + /** + * 清理所有未使用的锁 + */ + public void clearAll() { + writeLock.lock(); + try { + for (Iterator>> it = keys.entrySet().iterator(); it.hasNext(); ) { + Map.Entry> entry = it.next(); + WeakReference reference = entry.getValue(); + if (reference == null) { + continue; + } + if (reference.get() == null || reference.isEnqueued()) { + it.remove(); + } + } + } finally { + writeLock.unlock(); + } + } + + private ReentrantLock createLock(Key key) { + writeLock.lock(); + try { + WeakReference reference = keys.get(key); + if (reference != null) { + ReentrantLock lock = reference.get(); + if (lock != null) { + return lock; + } + } + + ReentrantLock lock = new ReentrantLock(); + reference = new WeakReference(lock); + keys.put(key, reference); + return lock; + } finally { + writeLock.unlock(); + } + } + +} diff --git a/src/test/java/org/beetl/core/KeyLockTest.java b/src/test/java/org/beetl/core/KeyLockTest.java new file mode 100644 index 0000000000000000000000000000000000000000..36ac93d5da3de71d61d183a4b21ba6d4d22f099d --- /dev/null +++ b/src/test/java/org/beetl/core/KeyLockTest.java @@ -0,0 +1,85 @@ +package org.beetl.core; + +import junit.framework.TestCase; + +import java.util.concurrent.locks.ReentrantLock; + +public class KeyLockTest extends TestCase { + + public void testLockFor() { + final KeyLock keyLock = new KeyLock(); + for (int key = 0; key < 10; key++) { + final int threadKey = key; + final int[] value = new int[]{ + 0 + }; + final int count = 10; + final Thread[] threads = new Thread[count]; + for (int i = 0; i < count; i++) { + threads[i] = new Thread() { + @Override + public void run() { + ReentrantLock lock = keyLock.lockFor(threadKey); + for (int j = 0; j < count; j++) { + lock.lock(); + try { + value[0]++; + } finally { + lock.unlock(); + } + } + } + }; + threads[i].start(); + } + + new Thread() { + @Override + public void run() { + for (Thread thread : threads) { + try { + thread.join(); + } catch (Exception e) { + } + } + System.out.println(value[0]); + assert value[0] == count * count; + } + }.start(); + } + + try { + Thread.sleep(10000L); + } catch (Exception e) { + } + } + + public void testClear() { + final KeyLock keyLock = new KeyLock(); + ReentrantLock lock = keyLock.lockFor(1); + ReentrantLock lock2 = keyLock.lockFor(2); + lock2.lock(); + try { + System.out.println(lock2); + System.out.println(keyLock.hasLock(2)); + } finally { + lock2.unlock(); + } + lock2 = null; + + try { + Thread.sleep(5000L); + System.gc(); + Thread.sleep(10000L); + } catch (Exception e) { + } + System.out.println(keyLock.hasLock(2)); + assert keyLock.hasLock(2) == false; + + assert keyLock.hasLock(1) == true; + lock = null; + + assert keyLock.remove(1) == true; + assert keyLock.hasLock(1) == false; + } +} \ No newline at end of file