扩展java的ThreadLocal以允许在所有线程中重置值 [英] Extending java's ThreadLocal to allow the values to be reset across all threads

查看:103
本文介绍了扩展java的ThreadLocal以允许在所有线程中重置值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

查看这个问题,我想我想要包装ThreadLocal来添加重置行为。



我想要有类似于ThreadLocal的东西,我可以从任何线程调用一个方法将所有值设置回相同的值。到目前为止我有这个:

  public class ThreadLocalFlag {

private ThreadLocal< Boolean>旗;
private List< Boolean> allValues = new ArrayList< Boolean>();

public ThreadLocalFlag(){
flag = new ThreadLocal< Boolean>(){
@Override protected Boolean initialValue(){
Boolean value = false;
allValues.add(value);
返回值;
}
};
}

public boolean get(){
return flag.get();
}

public void set(布尔值){
flag.set(value);
}

public void setAll(布尔值){
for(Boolean tlValue:allValues){
tlValue = value;
}
}
}

我很担心原语的自动装箱可能意味着我存储在列表中的副本如果我尝试设置它们,则不会引用ThreadLocal引用的相同变量。我还没有测试过这段代码,并且在我继续走这条道路之前,我正在寻找一些专家建议。



有人会问为什么你在做这个吗?我正在一个框架中工作,其他线程回调到我的代码中,我没有对它们的引用。我想定期更新他们使用的ThreadLocal变量中的值,因此执行该更新需要使用该变量的线程进行更新。我只需要一种方法来通知所有这些线程他们的ThreadLocal变量是陈旧的。






我很荣幸有这样的话最近对这个三年前的问题提出了新的批评,尽管我认为它的基调比专业还要小。我提供的解决方案在此期间没有发生任何事故。然而,必须有更好的方法来实现提出这个问题的目标,我邀请评论家提供明显更好的答案。为此,我将尝试更清楚地解决我试图解决的问题。



正如我之前提到的,我正在使用多个线程正在使用的框架我的代码,在我的控制之外。那个框架是QuickFIX / J,我正在实现应用程序界面。该接口定义了用于处理FIX消息的钩子,在我的使用中,框架被配置为多线程,因此可以同时处理与应用程序的每个FIX连接。



但是,QuickFIX / J框架仅为所有线程使用该接口的单个​​实例实例。我无法控制线程如何启动,并且每个都在为不同的连接提供不同的配置细节和其他状态。很自然地让一些经常访问但很少更新的状态存在于框架启动线程后加载其初始值的各种 ThreadLocal 中。 / p>

在组织的其他地方,我们有库代码,允许我们注册回调,以通知在运行时更改的配置详细信息。我想注册那个回调,当我收到它时,我想让所有线程知道是时候重新加载那些 ThreadLocal 的值,因为它们可能已经改变。该回调来自我无法控制的线程,就像QuickFIX / J线程一样。



我的解决方案使用 ThreadLocalFlag (一个包装的 ThreadLocal< AtomicBoolean> )仅用于通知其他线程可能是时候更新它们的值。回调调用 setAll(true),并且QuickFIX / J线程在开始更新时调用 set(false) 。我已经淡化了 ArrayList 的并发问题,因为列表添加到的唯一时间是在启动期间,并且我的用例小于列表的默认大小。 / p>

我想同样的任务可以用其他的线程通信技术完成,但是对于它正在做的事情,这看起来更实用。我欢迎其他解决方案。

解决方案

我对这个问题的答案质量感到失望;我找到了自己的解决方案。



我今天写了我的测试用例,发现我的问题代码中唯一的问题是 Boolean 。布尔值是不可变的,所以我的引用列表对我没有任何帮助。我查看了这个问题,并将我的代码更改为使用 AtomicBoolean ,现在一切都按预期工作。

  public class ThreadLocalFlag {

private ThreadLocal< AtomicBoolean>旗;
private List< AtomicBoolean> allValues = new ArrayList< AtomicBoolean>();

public ThreadLocalFlag(){
flag = new ThreadLocal< AtomicBoolean>(){
@Override protected AtomicBoolean initialValue(){
AtomicBoolean value = new AtomicBoolean() ;
allValues.add(value);
返回值;
}
};
}

public boolean get(){
return flag.get()。get();
}

public void set(boolean value){
flag.get()。set(value);
}

public void setAll(boolean value){
for(AtomicBoolean tlValue:allValues){
tlValue.set(value);
}
}
}

测试用例:

  public class ThreadLocalFlagTest {

private static ThreadLocalFlag flag = new ThreadLocalFlag();
private static boolean runThread = true;

@AfterClass
public static void tearDownOnce()抛出异常{
runThread = false;
flag = null;
}

/ **
* @throws如果测试有任何问题,则为例外
* /
@Test
public void testSetAll()抛出异常{
startThread(ThreadLocalFlagTest-1,false);
try {
Thread.sleep(1000L);
} catch(InterruptedException e){
// ignore
}
startThread(ThreadLocalFlagTest-2,true);
try {
Thread.sleep(1000L);
} catch(InterruptedException e){
// ignore
}
startThread(ThreadLocalFlagTest-3,false);
try {
Thread.sleep(1000L);
} catch(InterruptedException e){
// ignore
}
startThread(ThreadLocalFlagTest-4,true);
try {
Thread.sleep(8000L); //观察交替值
} catch(InterruptedException e){
// ignore
}
flag.setAll(true);
try {
Thread.sleep(8000L); //观察真值
} catch(InterruptedException e){
// ignore
}
flag.setAll(false);
try {
Thread.sleep(8000L); //观察错误值
} catch(InterruptedException e){
// ignore
}
}

private void startThread(String name,boolean值){
线程t =新线程(new RunnableCode(value));
t.setName(name);
t.start();
}

类RunnableCode实现Runnable {

private boolean initialValue;

RunnableCode(boolean value){
initialValue = value;
}

@Override
public void run(){
flag.set(initialValue);
while(runThread){
System.out.println(Thread.currentThread()。getName()+:+ flag.get());
try {
Thread.sleep(4000L);
} catch(InterruptedException e){
// ignore
}
}
}
}
}


After looking at this question, I think I want to wrap ThreadLocal to add a reset behavior.

I want to have something similar to a ThreadLocal, with a method I can call from any thread to set all the values back to the same value. So far I have this:

public class ThreadLocalFlag {

    private ThreadLocal<Boolean> flag;
    private List<Boolean> allValues = new ArrayList<Boolean>();

    public ThreadLocalFlag() {
        flag = new ThreadLocal<Boolean>() {
            @Override protected Boolean initialValue() {
                Boolean value = false;
                allValues.add(value);
                return value;
            }
        };
    }

    public boolean get() {
        return flag.get();
    }

    public void set(Boolean value) {
        flag.set(value);
    }

    public void setAll(Boolean value) {
        for (Boolean tlValue : allValues) {
            tlValue = value;
        }
    }
}

I'm worried that the autoboxing of the primitive may mean the copies I've stored in the list will not reference the same variables referenced by the ThreadLocal if I try to set them. I've not yet tested this code, and with something tricky like this I'm looking for some expert advice before I continue down this path.

Someone will ask "Why are you doing this?". I'm working in a framework where there are other threads that callback into my code, and I don't have references to them. Periodically I want to update the value in a ThreadLocal variable they use, so performing that update requires that the thread which uses the variable do the updating. I just need a way to notify all these threads that their ThreadLocal variable is stale.


I'm flattered that there is new criticism recently regarding this three year old question, though I feel the tone of it is a little less than professional. The solution I provided has worked without incident in production during that time. However, there are bound to be better ways to achieve the goal that prompted this question, and I invite the critics to supply an answer that is clearly better. To that end, I will try to be more clear about the problem I was trying to solve.

As I mentioned earlier, I was using a framework where multiple threads are using my code, outside my control. That framework was QuickFIX/J, and I was implementing the Application interface. That interface defines hooks for handling FIX messages, and in my usage the framework was configured to be multithreaded, so that each FIX connection to the application could be handled simultaneously.

However, the QuickFIX/J framework only uses a single instance of my implementation of that interface for all the threads. I'm not in control of how the threads get started, and each is servicing a different connection with different configuration details and other state. It was natural to let some of that state, which is frequently accessed but seldom updated, live in various ThreadLocals that load their initial value once the framework has started the thread.

Elsewhere in the organization, we had library code to allow us to register for callbacks for notification of configuration details that change at runtime. I wanted to register for that callback, and when I received it, I wanted to let all the threads know that it's time to reload the values of those ThreadLocals, as they may have changed. That callback comes from a thread I don't control, just like the QuickFIX/J threads.

My solution below uses ThreadLocalFlag (a wrapped ThreadLocal<AtomicBoolean>) solely to signal the other threads that it may be time to update their values. The callback calls setAll(true), and the QuickFIX/J threads call set(false) when they begin their update. I have downplayed the concurrency issues of the ArrayList because the only time the list is added to is during startup, and my use case was smaller than the default size of the list.

I imagine the same task could be done with other interthread communication techniques, but for what it's doing, this seemed more practical. I welcome other solutions.

解决方案

I'm disappointed in the quality of the answers received for this question; I have found my own solution.

I wrote my test case today, and found the only issue with the code in my question is the Boolean. Boolean is not mutable, so my list of references wasn't doing me any good. I had a look at this question, and changed my code to use AtomicBoolean, and now everything works as expected.

public class ThreadLocalFlag {

    private ThreadLocal<AtomicBoolean> flag;
    private List<AtomicBoolean> allValues = new ArrayList<AtomicBoolean>();

    public ThreadLocalFlag() {
        flag = new ThreadLocal<AtomicBoolean>() {
            @Override protected AtomicBoolean initialValue() {
                AtomicBoolean value = new AtomicBoolean();
                allValues.add(value);
                return value;
            }
        };
    }

    public boolean get() {
        return flag.get().get();
    }

    public void set(boolean value) {
        flag.get().set(value);
    }

    public void setAll(boolean value) {
        for (AtomicBoolean tlValue : allValues) {
            tlValue.set(value);
        }
    }
}

Test case:

public class ThreadLocalFlagTest {

    private static ThreadLocalFlag flag = new ThreadLocalFlag();
    private static boolean runThread = true;

    @AfterClass
    public static void tearDownOnce() throws Exception {
        runThread = false;
        flag = null;
    }

    /**
     * @throws Exception if there is any issue with the test
     */
    @Test
    public void testSetAll() throws Exception {
        startThread("ThreadLocalFlagTest-1", false);
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            //ignore
        }
        startThread("ThreadLocalFlagTest-2", true);
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            //ignore
        }
        startThread("ThreadLocalFlagTest-3", false);
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            //ignore
        }
        startThread("ThreadLocalFlagTest-4", true);
        try {
            Thread.sleep(8000L); //watch the alternating values
        } catch (InterruptedException e) {
            //ignore
        }
        flag.setAll(true);
        try {
            Thread.sleep(8000L); //watch the true values
        } catch (InterruptedException e) {
            //ignore
        }
        flag.setAll(false);
        try {
            Thread.sleep(8000L); //watch the false values
        } catch (InterruptedException e) {
            //ignore
        }
    }

    private void startThread(String name, boolean value) {
        Thread t = new Thread(new RunnableCode(value));
        t.setName(name);
        t.start();
    }

    class RunnableCode implements Runnable {

        private boolean initialValue;

        RunnableCode(boolean value) {
            initialValue = value;
        }

        @Override
        public void run() {
            flag.set(initialValue);
            while (runThread) {
                System.out.println(Thread.currentThread().getName() + ": " + flag.get());
                try {
                    Thread.sleep(4000L);
                } catch (InterruptedException e) {
                    //ignore
                }
            }
        }
    }
}

这篇关于扩展java的ThreadLocal以允许在所有线程中重置值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆