执行程序通道 - InheritableThreadLocal问题

时间:2016-05-04 12:16:46

标签: spring-integration

我正在使用执行程序通道进行并行调用。

<task:executor id="taskExecutor" pool-size="5"/>
<int:channel id="executorChannel">
    <int:dispatcher task-executor="taskExecutor"/>
</int:channel>

我使用的是InheritableThreadLocal变量,由于池大小声明为5,因此在第6次调用后会重置为初始值。

换句话说,1-10中的线程本地值读取为1-5并再次读取1-5。 除ThreadLocal变量问题外,一切正常。

请告知修复。

以下是示例类: 1. HeaderContext - 存储InheritableThreadLocal变量

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。测试类

@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背景

<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"/>

输出

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

1 个答案:

答案 0 :(得分:1)

嗯,我确定当您在服务中打印ThreadId时,您会因为您的目的而重复使用线程而感到惊讶:

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

这是简单测试的结果:

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));
}

现在让我们来看看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. 

因此,每个线程都有自己的变量副本,并在一个线程中更改它们不会影响另一个线程。

主线程中的InheritableThreadLocal与普通ThreadLocal一样。只有这样,新生成的孩子Thread才能获得main ThreadLocal的当前副本。这就是我们为每个新线程看到一个新值的原因,但是当重用那个子线程时,就像ThreadPool一样,我们仍然会看到它自己的旧值。

请阅读更多有关此事的RTFM!

希望明确