ThreadLocal 和非线程安全 API

ThreadLocal and non-thread safe API

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

Foo instance = new Foo();

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

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

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

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

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

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

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

ThreadLocal 不会解决你的问题,ThreadLocal 只是为每个线程独立存储不同的实例。所以在你的情况下,如果你在第 3rd 方图书馆级别共享资源,那将无法解决问题。

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

就这样:

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

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