From 20cd3f51a619a95409c986c1cd549a807f0d107e Mon Sep 17 00:00:00 2001 From: butnet Date: Sat, 6 Jun 2020 01:46:39 +0800 Subject: [PATCH 1/3] =?UTF-8?q?Fixed:=20=E9=94=81=E4=B8=8E=E4=BA=8C?= =?UTF-8?q?=E6=AC=A1=E6=A3=80=E6=9F=A5=E6=9C=AA=E9=87=8D=E6=96=B0=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=80=BC=EF=BC=8C=E9=80=A0=E6=88=90=E5=A4=9A=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=B8=8Bprogram=E8=A2=AB=E5=A4=9A=E6=AC=A1=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改key的锁方式,并且在锁内重新获取program再做二次检查 --- .../java/org/beetl/core/GroupTemplate.java | 31 ++++- src/main/java/org/beetl/core/KeyLock.java | 121 ++++++++++++++++++ src/test/java/org/beetl/core/KeyLockTest.java | 85 ++++++++++++ 3 files changed, 232 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/beetl/core/KeyLock.java create mode 100644 src/test/java/org/beetl/core/KeyLockTest.java diff --git a/src/main/java/org/beetl/core/GroupTemplate.java b/src/main/java/org/beetl/core/GroupTemplate.java index 5dfd1d9..463bf49 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 0000000..24e80fc --- /dev/null +++ b/src/main/java/org/beetl/core/KeyLock.java @@ -0,0 +1,121 @@ +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 + */ +public class KeyLock { + private final WeakHashMap> keys = new WeakHashMap>(); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); + private final ReentrantReadWriteLock.WriteLock writeLock = lock.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 || 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.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 0000000..36ac93d --- /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 -- Gitee From 06c8d4f6c0ddc16de52835894a823c51bd234af5 Mon Sep 17 00:00:00 2001 From: butnet Date: Sat, 6 Jun 2020 13:30:53 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/beetl/core/KeyLock.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/beetl/core/KeyLock.java b/src/main/java/org/beetl/core/KeyLock.java index 24e80fc..383d269 100644 --- a/src/main/java/org/beetl/core/KeyLock.java +++ b/src/main/java/org/beetl/core/KeyLock.java @@ -11,6 +11,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * 按指定Key,生成锁,如果锁不再引用,由系统GC自动回收,也可手动回收(调用clear方法)不再使用的锁 * * @param + * @author butnet */ public class KeyLock { private final WeakHashMap> keys = new WeakHashMap>(); @@ -70,7 +71,10 @@ public class KeyLock { writeLock.lock(); try { WeakReference reference = keys.get(key); - if (reference != null || reference.get() == null || reference.isEnqueued()) { + if (reference == null) { + return false; + } + if (reference.get() == null || reference.isEnqueued()) { keys.remove(key); return true; } @@ -89,6 +93,9 @@ public class KeyLock { 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(); } -- Gitee From 2bb6f7bc743feb79fceaed839be689ecd65d138c Mon Sep 17 00:00:00 2001 From: butnet Date: Sat, 6 Jun 2020 13:33:01 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/beetl/core/KeyLock.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/beetl/core/KeyLock.java b/src/main/java/org/beetl/core/KeyLock.java index 383d269..2645dad 100644 --- a/src/main/java/org/beetl/core/KeyLock.java +++ b/src/main/java/org/beetl/core/KeyLock.java @@ -15,9 +15,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; */ public class KeyLock { private final WeakHashMap> keys = new WeakHashMap>(); - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); - private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock(); + private final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock(); /** * 获取指定Key的锁对象,如果不存在,创建一个 -- Gitee