Reputation: 139
I am using executor channel for parallel calls.
<task:executor id="taskExecutor" pool-size="5"/>
<int:channel id="executorChannel">
<int:dispatcher task-executor="taskExecutor"/>
</int:channel>
I am using InheritableThreadLocal variable, it is reset to the initial value after 6th call because of the pool-size declared as 5.
In other words, thread local value from 1-10 read as 1-5 and again 1-5. Other than ThreadLocal variable issue, everything is working fine.
please advise on the fix.
Here is the sample Classes: 1. HeaderContext - to store InheritableThreadLocal variable
public final class HeaderContext {
private HeaderContext() {}
private static final InheritableThreadLocal<String> GID = new InheritableThreadLocal<String>() {
@Override
protected String initialValue() {
return new String();
}
};
public static void clear() {
GID.remove();
}
public static void setGID(String gid) {
GID.set(gid);
}
public static String getGID() {
return GID.get();
}
}
2. TestGateway
@Component
public interface TestGateway {
String testMethod(String name);
}
3. TestActivator
@Component
public class TestActivator {
public String testMethod(String name){
System.out.println("GID from Inheritable thread local ->"+HeaderContext.getGID());
return name;
}
}
4. Test Class
@ContextConfiguration(locations = { "classpath:exec-channel-si.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class ThreadLocalGatewayTest {
@Autowired
TestGateway testGateway;
@Test
public void testBrokerageGateway() throws InterruptedException {
for(int i=1;i<=5;i++){
try {
HeaderContext.setGID("gid"+i);
System.out.println("done->" + testGateway.testMethod("name"+i));
} finally {
HeaderContext.clear();
}
Thread.sleep(2000);
}
}
}
5. SI context
<task:executor id="taskExecutor" pool-size="2"/>
<context:component-scan base-package="sample.test"/>
<int:channel id="executorChannel">
<int:dispatcher task-executor="taskExecutor"/>
</int:channel>
<int:gateway id="testGateway"
service-interface="sample.test.TestGateway"
default-request-channel="executorChannel">
</int:gateway>
<int:service-activator input-channel="executorChannel" ref="testActivator"/>
Output
GID from Inheritable thread local ->gid1
output->name1
GID from Inheritable thread local ->gid2
output->name2
GID from Inheritable thread local ->gid1
output->name3
GID from Inheritable thread local ->gid2
output->name4
GID from Inheritable thread local ->gid1
output->name5
Upvotes: 1
Views: 561
Reputation: 121542
Well, I'm sure when you print ThreadId
in your service, you won't be surprised that you reuse thread for your purpose:
name-> name1 in the inheritable thread local -> gid1 for thread: pool-1-thread-1
name-> name2 in the inheritable thread local -> gid2 for thread: pool-1-thread-2
name-> name3 in the inheritable thread local -> gid3 for thread: pool-1-thread-3
name-> name4 in the inheritable thread local -> gid4 for thread: pool-1-thread-4
name-> name5 in the inheritable thread local -> gid5 for thread: pool-1-thread-5
name-> name6 in the inheritable thread local -> gid4 for thread: pool-1-thread-4
name-> name10 in the inheritable thread local -> gid1 for thread: pool-1-thread-1
name-> name8 in the inheritable thread local -> gid2 for thread: pool-1-thread-2
name-> name9 in the inheritable thread local -> gid3 for thread: pool-1-thread-3
name-> name7 in the inheritable thread local -> gid5 for thread: pool-1-thread-5
That is result of the simple test:
private static final ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
@Test
public void testInheritableThreadLocal() throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
final CountDownLatch stopLatch = new CountDownLatch(10);
for (int i = 1; i <= 10; i++) {
final int j = i;
try {
threadLocal.set("gid" + i);
executorService.execute(() -> {
System.out.println("name-> " + "name " + j + " in the inheritable thread local -> "
+ threadLocal.get() + " for thread: " + Thread.currentThread().getName());
stopLatch.countDown();
});
}
finally {
threadLocal.remove();
}
}
assertTrue(stopLatch.await(10, TimeUnit.SECONDS));
}
Now let's take a look into ThreadLocal
JavaDocs:
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable.
So, each thread has its own copy of variables and change to them in one thread doesn't affect another thread.
The InheritableThreadLocal
in the main Thread is as a normal ThreadLocal
. Only with that gain that a new spawned child Thread
will get a current copy of the main
ThreadLocal
. That's why we see a fresh values for each new thread, but when reuse that child thread, like it is in case of ThreadPool
, we still see its own old value.
Please, read more RTFM on the matter!
Hope that clear
Upvotes: 1