【问题标题】:ThreadLocal and non-thread safe APIThreadLocal 和非线程安全 API
【发布时间】:2016-09-23 06:54:21
【问题描述】:

所以我有一个我目前正在使用的非线程安全 API(一些供应商软件),我们目前使用它的方式是每个线程一个对象。即每个线程都有:

Foo instance = new Foo();

但是,这似乎不适用于这个特定的库。它的一些非线程安全位仍然出现在头上,所以我假设这个库中有一些静态值。在一些我们知道它有问题的地方,我们目前正在使用 ReentrantLock 在需要时锁定类。即。

public class Bar {
    protected static final ReentrantLock lock = new ReentrantLock();

    public void process() {
        Foo instance = new Foo();
        boolean locked = false;
        try{
            if(SomeCondition) {
                locked = true;
                Bar.lock.lock();
            }

            *//rest of the processing goes here

        } finally {
            if(locked){
                Bar.lock.unlock();
            }
        }
    }
}

我的问题是:在所讨论的类不是线程安全的这种情况下,即使在创建所述类的新实例时,使用锁定是否更好,或者我应该看起来使用 ThreadLocals 代替? ThreadLocals 甚至会缓解我的实际问题吗?类的 ThreadLocal 版本是否实际上强制类的静态区域本质上是非静态的?

【问题讨论】:

  • 不,ThreadLocal 不会强制类的静态区域本质上是非静态的
  • 为什么你认为ThreadLocal 对每个线程中的本地对象的行为会有所不同?
  • @StrangerintheQ - 好吧,那我想这回答了我的问题
  • 如果您已经在每个线程使用一个实例,则供应商代码是线程 hostile,而不仅仅是线程不安全。
  • 代替ReentrantLock,用代理包装类并创建所有方法synchronized,然后在所有线程中使用该单个实例可能更容易。

标签: java multithreading static thread-safety thread-local


【解决方案1】:

ThreadLocal 所做的只是创建一个查找,每个线程都可以在其中找到自己的对象实例,因此没有线程必须共享。从概念上讲,您可以将其视为以线程 ID 为键的映射。

在某些情况下,让每个线程使用自己的对象是一个很好的策略,在 JCIP 书中称为“线程限制”。一个常见的例子是 SimpleDateFormat 对象没有被设计为线程安全的,使用它们的并发线程会产生不好的结果。使用 ThreadLocal 可以让每个线程使用自己的 DateFormat,see this question for an example。

但如果您的问题是对象引用静态字段,那么这些静态字段存在于类上,而不是实例上,因此使用 ThreadLocal 不会减少共享。

如果您的每个线程都使用自己的类加载器,那么每个线程都会有自己的类,并且其上的静态字段不会被共享。否则,您对类的锁定似乎是合理的(尽管考虑到您的所有线程都将争夺同一个锁,但可能不会很快)。 最好的方法是与供应商合作,让他们修复损坏的代码。

【讨论】:

  • 有道理。感谢您的输入!是的,我正在探索这个,因为锁定不一定很快,所以我希望它可以帮助这个部门。
  • 即使是静态锁定也不能保证它会正常工作。根据通过静态变量跟踪的信息,您仍然可以在线程之间获取信息。
  • @jtahlborn:是的,锁定粒度可能必须非常宽,以至于多线程毫无意义。我会调查自定义类加载器的角度。
【解决方案2】:

ThreadLocal 不会解决您的问题,ThreadLocal 只是为每个线程独立存储不同的实例。因此,在您的情况下,如果您在 3rd 方库级别共享资源,则无法解决问题。

一个简单的同步监视器将解决这个问题,因为您想避免并发访问该库,但要注意监视器的性能损失 - 只有一个线程可以同时访问该库

只要做:

public class Bar {
    private static final Object LOCK = new Object();

    public void process() {
        synchronized(LOCK) {
            Foo instance = new Foo();
            instance.myMethod();            
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-30
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多