diff --git a/pom.xml b/pom.xml index af793cb30..5a417f740 100644 --- a/pom.xml +++ b/pom.xml @@ -95,6 +95,13 @@ 3.23.1-GA true + + io.netty + netty-all + 4.0.20.Final + provided + true + junit diff --git a/pom4ide.xml b/pom4ide.xml index f2e731d0c..bdaa1a9c7 100644 --- a/pom4ide.xml +++ b/pom4ide.xml @@ -95,6 +95,13 @@ 3.23.1-GA true + + io.netty + netty-all + 4.0.20.Final + provided + true + junit diff --git a/src/main/java/com/alibaba/ttl/TransmittableThreadLocal.java b/src/main/java/com/alibaba/ttl/TransmittableThreadLocal.java index c310157eb..991211d8a 100644 --- a/src/main/java/com/alibaba/ttl/TransmittableThreadLocal.java +++ b/src/main/java/com/alibaba/ttl/TransmittableThreadLocal.java @@ -1,5 +1,8 @@ package com.alibaba.ttl; +import com.alibaba.ttl.internal.TtlValue; +import com.alibaba.ttl.internal.TtlValueFactory; + import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -14,6 +17,10 @@ *

* Note: {@link TransmittableThreadLocal} extends {@link java.lang.InheritableThreadLocal}, * so {@link TransmittableThreadLocal} first is a {@link java.lang.InheritableThreadLocal}. + *

+ * If you have netty in the runtime and {@link io.netty.util.internal.FastThreadLocal} is supported. You can store + * {@link TransmittableThreadLocal} in {@link io.netty.util.internal.FastThreadLocal} to give up inheritance for better performance. + *

* * @author Jerry Lee (oldratlee at gmail dot com) * @see TtlRunnable @@ -66,7 +73,7 @@ protected void afterExecute() { @Override public final T get() { - T value = super.get(); + T value = ttlValue != null ? ttlValue.get() : super.get(); if (null != value) { addValue(); } @@ -75,7 +82,11 @@ public final T get() { @Override public final void set(T value) { - super.set(value); + if (ttlValue != null) { + ttlValue.set(value); + } else { + super.set(value); + } if (null == value) { // may set null to remove value removeValue(); } else { @@ -86,7 +97,11 @@ public final void set(T value) { @Override public final void remove() { removeValue(); - super.remove(); + if (ttlValue != null) { + ttlValue.remove(); + } else { + super.remove(); + } } private void superRemove() { @@ -97,6 +112,8 @@ private T copyValue() { return copy(get()); } + private TtlValue ttlValue = TtlValueFactory.create(); + // Note about holder: // 1. The value of holder is type Map, ?> (WeakHashMap implementation), // but it is used as *set*. diff --git a/src/main/java/com/alibaba/ttl/internal/FastThreadLocalValue.java b/src/main/java/com/alibaba/ttl/internal/FastThreadLocalValue.java new file mode 100644 index 000000000..de2992caa --- /dev/null +++ b/src/main/java/com/alibaba/ttl/internal/FastThreadLocalValue.java @@ -0,0 +1,29 @@ +package com.alibaba.ttl.internal; + +import io.netty.util.internal.FastThreadLocal; + +/** + * {@link FastThreadLocal} implementation for {@link com.alibaba.ttl.TransmittableThreadLocal} value holder + * + * @author Yang Fang (snoop dot fy at gmail dot com) + * @since 2.7.0 + */ +public class FastThreadLocalValue implements TtlValue { + + private FastThreadLocal holder = new FastThreadLocal(); + + @Override + public T get() { + return holder.get(); + } + + @Override + public void set(T t) { + holder.set(t); + } + + @Override + public void remove() { + holder.remove(); + } +} diff --git a/src/main/java/com/alibaba/ttl/internal/TtlValue.java b/src/main/java/com/alibaba/ttl/internal/TtlValue.java new file mode 100644 index 000000000..d8c41496e --- /dev/null +++ b/src/main/java/com/alibaba/ttl/internal/TtlValue.java @@ -0,0 +1,17 @@ +package com.alibaba.ttl.internal; + +/** + * Hold {@link com.alibaba.ttl.TransmittableThreadLocal} value, can be implemented in varied ways depending on the runtime. + * + * @author Yang Fang (snoop dot fy at gmail dot com) + * @since 2.7.0 + */ +public interface TtlValue { + + T get(); + + void set(T t); + + void remove(); + +} diff --git a/src/main/java/com/alibaba/ttl/internal/TtlValueFactory.java b/src/main/java/com/alibaba/ttl/internal/TtlValueFactory.java new file mode 100644 index 000000000..5e95dfa92 --- /dev/null +++ b/src/main/java/com/alibaba/ttl/internal/TtlValueFactory.java @@ -0,0 +1,49 @@ +package com.alibaba.ttl.internal; + +/** + * {@link com.alibaba.ttl.TransmittableThreadLocal} value holder factory. + * + *

+ * If there is netty in the runtime and + * {@link io.netty.util.internal.FastThreadLocal} is supported. You can just set + * + *

-Dttl.fastthreadlocal.enable=true
+ * + * to enable FastThreadLocal mode for better performance. + *

+ * + *

+ * Caution: If FastThreadLocal mode is enabled, {@link com.alibaba.ttl.TransmittableThreadLocal} + * will NEVER be inheritable! + *

+ * + * @author Yang Fang (snoop dot fy at gmail dot com) + * @since 2.7.0 + */ +public class TtlValueFactory { + + private static final String FAST_THREAD_LOCAL_ENABLE = "ttl.fastthreadlocal.enable"; + + private static final boolean IS_FAST_THREAD_LOCAL_SUPPORT = isClassPresent("io.netty.util.internal.FastThreadLocal"); + + public static TtlValue create() { + if (isFastThreadLocalEnabled() && IS_FAST_THREAD_LOCAL_SUPPORT) { + return new FastThreadLocalValue(); + } else { + return null; + } + } + + public static boolean isFastThreadLocalEnabled() { + return Boolean.parseBoolean(System.getProperty(FAST_THREAD_LOCAL_ENABLE, "false")); + } + + private static boolean isClassPresent(String className) { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } +} diff --git a/src/test/java/com/alibaba/ttl/TtlCallableTest.kt b/src/test/java/com/alibaba/ttl/TtlCallableTest.kt index a4c275c1d..2ae1ebb82 100644 --- a/src/test/java/com/alibaba/ttl/TtlCallableTest.kt +++ b/src/test/java/com/alibaba/ttl/TtlCallableTest.kt @@ -1,6 +1,7 @@ package com.alibaba.ttl import com.alibaba.* +import com.alibaba.ttl.internal.TtlValueFactory import com.alibaba.ttl.testmodel.Call import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.instanceOf @@ -33,12 +34,14 @@ class TtlCallableTest { val ret = ttlCallable.call() assertEquals("ok", ret) + // inheritable is not supported by FastThreadLocal mode + if (!TtlValueFactory.isFastThreadLocalEnabled()) { + // child Inheritable + assertChildTtlValues("1", call.copied) - // child Inheritable - assertChildTtlValues("1", call.copied) - - // child do not effect parent - assertParentTtlValues(copyTtlValues(ttlInstances)) + // child do not effect parent + assertParentTtlValues(copyTtlValues(ttlInstances)) + } } @Test diff --git a/src/test/java/com/alibaba/ttl/TtlRunnableTest.kt b/src/test/java/com/alibaba/ttl/TtlRunnableTest.kt index db6575cfa..da0ec54a1 100644 --- a/src/test/java/com/alibaba/ttl/TtlRunnableTest.kt +++ b/src/test/java/com/alibaba/ttl/TtlRunnableTest.kt @@ -1,6 +1,7 @@ package com.alibaba.ttl import com.alibaba.* +import com.alibaba.ttl.internal.TtlValueFactory import com.alibaba.ttl.testmodel.* import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.instanceOf @@ -33,11 +34,14 @@ class TtlRunnableTest { ttlRunnable.run() - // child Inheritable - assertChildTtlValues("1", task.copied) + // inheritable is not supported by FastThreadLocal mode + if (!TtlValueFactory.isFastThreadLocalEnabled()) { + // child Inheritable + assertChildTtlValues("1", task.copied) - // child do not effect parent - assertParentTtlValues(copyTtlValues(ttlInstances)) + // child do not effect parent + assertParentTtlValues(copyTtlValues(ttlInstances)) + } } @Test @@ -55,8 +59,11 @@ class TtlRunnableTest { thread1.join() - // child Inheritable - assertChildTtlValues("1", task.copied) + // inheritable is not supported by FastThreadLocal mode + if (!TtlValueFactory.isFastThreadLocalEnabled()) { + // child Inheritable + assertChildTtlValues("1", task.copied) + } // child do not effect parent assertParentTtlValues(copyTtlValues(ttlInstances)) @@ -77,8 +84,11 @@ class TtlRunnableTest { submit.get() - // child Inheritable - assertChildTtlValues("1", task.copied) + // inheritable is not supported by FastThreadLocal mode + if (!TtlValueFactory.isFastThreadLocalEnabled()) { + // child Inheritable + assertChildTtlValues("1", task.copied) + } // child do not effect parent assertParentTtlValues(copyTtlValues(ttlInstances)) @@ -102,8 +112,11 @@ class TtlRunnableTest { submit.get() - // child Inheritable - assertChildTtlValues("1", task.copied) + // inheritable is not supported by FastThreadLocal mode + if (!TtlValueFactory.isFastThreadLocalEnabled()) { + // child Inheritable + assertChildTtlValues("1", task.copied) + } // child do not effect parent assertParentTtlValues(copyTtlValues(ttlInstances)) @@ -168,11 +181,14 @@ class TtlRunnableTest { val submit = executorService.submit(ttlRunnable) submit.get() - // child Inheritable - assertEquals(3, task.copied.size.toLong()) - assertEquals(FooPojo(PARENT_CREATE_UNMODIFIED_IN_CHILD, 1), task.copied[PARENT_CREATE_UNMODIFIED_IN_CHILD]) - assertEquals(FooPojo(PARENT_CREATE_MODIFIED_IN_CHILD + "1", 2), task.copied[PARENT_CREATE_MODIFIED_IN_CHILD]) - assertEquals(FooPojo(CHILD_CREATE + 1, 3), task.copied[CHILD_CREATE + 1]) + // inheritable is not supported by FastThreadLocal mode + if (!TtlValueFactory.isFastThreadLocalEnabled()) { + // child Inheritable + assertEquals(3, task.copied.size.toLong()) + assertEquals(FooPojo(PARENT_CREATE_UNMODIFIED_IN_CHILD, 1), task.copied[PARENT_CREATE_UNMODIFIED_IN_CHILD]) + assertEquals(FooPojo(PARENT_CREATE_MODIFIED_IN_CHILD + "1", 2), task.copied[PARENT_CREATE_MODIFIED_IN_CHILD]) + assertEquals(FooPojo(CHILD_CREATE + 1, 3), task.copied[CHILD_CREATE + 1]) + } // child do not effect parent val copied = copyTtlValues(ttlInstances) diff --git a/src/test/java/com/alibaba/ttl/reported_bugs/Bug70_Test.kt b/src/test/java/com/alibaba/ttl/reported_bugs/Bug70_Test.kt index 08b836f6b..d12a93cff 100644 --- a/src/test/java/com/alibaba/ttl/reported_bugs/Bug70_Test.kt +++ b/src/test/java/com/alibaba/ttl/reported_bugs/Bug70_Test.kt @@ -2,6 +2,7 @@ package com.alibaba.ttl.reported_bugs import com.alibaba.ttl.TransmittableThreadLocal import com.alibaba.ttl.TtlRunnable +import com.alibaba.ttl.internal.TtlValueFactory import org.junit.Assert.assertEquals import org.junit.Test import java.util.concurrent.Executors @@ -29,15 +30,18 @@ class Bug70_Test { .get() assertEquals(hello, task1.get()) - val taskRef = AtomicReference>() - val thread = Thread { - val task2 = FutureTask { threadLocal.get() } - val runnable = TtlRunnable.get(task2, false, false) - executorService.submit(runnable) - taskRef.set(task2) + // inheritable is not supported by FastThreadLocal mode + if (!TtlValueFactory.isFastThreadLocalEnabled()) { + val taskRef = AtomicReference>() + val thread = Thread { + val task2 = FutureTask { threadLocal.get() } + val runnable = TtlRunnable.get(task2, false, false) + executorService.submit(runnable) + taskRef.set(task2) + } + thread.start() + thread.join() + assertEquals(hello, taskRef.get().get()) } - thread.start() - thread.join() - assertEquals(hello, taskRef.get().get()) } }