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..d7e35797e 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
@@ -23,6 +30,9 @@
public class TransmittableThreadLocal extends InheritableThreadLocal {
private static final Logger logger = Logger.getLogger(TransmittableThreadLocal.class.getName());
+ // Hold the real value to support special optimization for thread local
+ private final TtlValue ttlValue = TtlValueFactory.create();
+
/**
* Computes the value for this transmittable thread-local variable
* as a function of the source thread's value at the time the task
@@ -66,7 +76,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 +85,7 @@ public final T get() {
@Override
public final void set(T value) {
- super.set(value);
+ setTtlValue(value);
if (null == value) { // may set null to remove value
removeValue();
} else {
@@ -83,10 +93,22 @@ public final void set(T value) {
}
}
+ private void setTtlValue(T value) {
+ if (ttlValue != null) {
+ ttlValue.set(value);
+ } else {
+ super.set(value);
+ }
+ }
+
@Override
public final void remove() {
removeValue();
- super.remove();
+ if (ttlValue != null) {
+ ttlValue.remove();
+ } else {
+ super.remove();
+ }
}
private void superRemove() {
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())
}
}