Java的ThreadLocal返回null值。

huangapple go评论81阅读模式
英文:

Java ThreadLocal returns null value

问题

我有一些需要在我的代码中并行执行的复杂计算,并且需要一些机制来正确记录数据到我的自定义数据结构中。我不想将这个数据结构传递给我的计算中的每个类(有很多类),也不想在每个地方都创建一个并返回它,所以我想出了一个将其存储在静态ThreadLocal中以便我的代码在作业中访问的方法。它看起来像这样:

public class MyLoggerWrapper
{
    private static ThreadLocal<MyLogger> threadLocalMyLogger;
    
    public static MyLogger myLogger() 
    {
        return threadLocalMyLogger.get();
    }
    
    static void init(final SomeParameter parameter) 
    {        
        MyLoggerWrapper.threadLocalMyLogger = new ThreadLocal<>();
        MyLoggerWrapper.threadLocalMyLogger.set(new MyLogger(parameter));
    }
    
    static void remove() 
    {
        threadLocalMyLogger.remove();
    }
}

我的可调用类看起来像这样:

public class MyJob implements Callable<Something>
{
    @Override
    public Something call() throws Exception
    {
        try 
        {
            MyLoggerWrapper.init(someParameter);
            
            return ...doSomeWork();
        } 
        catch (final Exception e) 
        {
            ....doSomeOtherStuff();
        }
        finally 
        {
            MyLoggerWrapper.remove();
        }
    }
}

在需要记录某些内容的地方,我会这样调用静态记录器:

MyLoggerWrapper.myLogger().logStuff(); 

当我使用几个线程进行测试时,它工作得很好,但当我达到大约200-250个并行计算时,它开始抛出空指针异常,因为

MyLoggerWrapper.myLogger();

返回null。它永远不应该为null,因为我的作业的第一件事就是实例化一个。

(我特意没有使用ThreadLocal.withInitial(),因为在我的看法中,每个线程只有一个供应者,它可以被覆盖,而我需要不同的输入参数来执行不同的作业。)

有什么想法可能会导致这种情况吗?我是否对ThreadLocal的工作方式有所遗漏,或者是否不了解某种限制?

英文:

I have some complex calculation that needs to be done parallel in my code and I needed some mechanism to properly log stuff to my own data structure. I didn't want to pass this data structure down to every class my calculations are using (there are a lot), nor didn't want to create one in everywhere and return it, so I came up with an idea to store it in a static ThreadLocal that can be accessed by my code thats running inside a job. It looks something like this:

public class MyLoggerWrapper
{
	private static ThreadLocal&lt;MyLogger&gt; threadLocalMyLogger;
	
	public static MyLogger myLogger() 
	{
		return threadLocalMyLogger.get();
	}
	
	static void init(final SomeParameter parameter) 
	{		
		MyLoggerWrapper.threadLocalMyLogger = new ThreadLocal&lt;&gt;();
        MyLoggerWrapper.threadLocalMyLogger.set(new MyLogger(parameter));
	}
	
	static void remove() 
	{
		threadLocalMyLogger.remove();
	}
}

My callable class looks something like this:

public class MyJob implements Callable&lt;Something&gt;
{
    @Override
	public Something call() throws Exception
	{
		try 
		{
			MyLoggerWrapper.init(someParameter);
			
			return ...doSomeWork();
		} 
		catch (final Exception e) 
		{
			....doSomeOtherStuff();
		}
		finally 
		{
			MyLoggerWrapper.remove();
		}
	}
}

In places where i need to log something i would call the static logger like so:

MyLoggerWrapper.myLogger().logStuff(); 

When I was testing it with a few threads it was working fine, but when i hit around 200 - 250 parallel calculations it started to throw nullpointers because

MyLoggerWrapper.myLogger();

returned null. It is never supposed to be null since the first thing my job does is to instantiate one.

(I dint use ThreadLocal.withInitial() on purpose because it seem to me that only one supplier is present for every thread and it can be overridden, and I need different input parameters for different jobs.)

Any idea what might cause this? Am I missing something with how ThreadLocal works, or am i not aware of some kind of limitation?

答案1

得分: 0

Your code only requires one instance of ThreadLocal, you have many threads wiping the last initialised value of threadLocalMyLogger inside init().

尝试只使用一个最终声明,并移除每个线程的赋值:

private static final ThreadLocal threadLocalMyLogger = new ThreadLocal<>();

static void init(final SomeParameter parameter)
{
MyLoggerWrapper.threadLocalMyLogger.set(new MyLogger(parameter));
}

英文:

Your code only requires one instance of ThreadLocal, you have many threads wiping the last initialised value of threadLocalMyLogger inside init().

Try with one final declaration and remove the per Thread assignment:

private static final ThreadLocal&lt;MyLogger&gt; threadLocalMyLogger = new ThreadLocal&lt;&gt;(); 

static void init(final SomeParameter parameter) 
{       
    // MyLoggerWrapper.threadLocalMyLogger = new ThreadLocal&lt;&gt;();
    MyLoggerWrapper.threadLocalMyLogger.set(new MyLogger(parameter));
}

huangapple
  • 本文由 发表于 2023年5月26日 15:12:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76338420.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定