Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 何时以及如何使用ThreadLocal变量?_Java_Multithreading_Concurrency_Thread Local_Thread Confinement - Fatal编程技术网

Java 何时以及如何使用ThreadLocal变量?

Java 何时以及如何使用ThreadLocal变量?,java,multithreading,concurrency,thread-local,thread-confinement,Java,Multithreading,Concurrency,Thread Local,Thread Confinement,什么时候应该使用变量 它是如何使用的?说得很好:“访问[线程局部变量](通过其get或set方法)的每个线程都有自己的、独立初始化的变量副本” 当每个线程都必须有自己的副本时,可以使用一个。默认情况下,数据在线程之间共享。在Java中,如果每个线程都有一个不同的数据,那么您可以选择将该数据传递给每个需要(或可能需要)它的方法,或者将该数据与线程相关联。如果所有方法都需要传递一个公共的“上下文”变量,那么到处传递数据可能是可行的 如果不是这样的话,您可能不想用额外的参数将方法签名弄得杂乱无章。在非

什么时候应该使用变量

它是如何使用的?

说得很好:“访问[线程局部变量](通过其get或set方法)的每个线程都有自己的、独立初始化的变量副本”


当每个线程都必须有自己的副本时,可以使用一个。默认情况下,数据在线程之间共享。

在Java中,如果每个线程都有一个不同的数据,那么您可以选择将该数据传递给每个需要(或可能需要)它的方法,或者将该数据与线程相关联。如果所有方法都需要传递一个公共的“上下文”变量,那么到处传递数据可能是可行的

如果不是这样的话,您可能不想用额外的参数将方法签名弄得杂乱无章。在非线程世界中,您可以使用与全局变量相当的Java解决问题。在一个线程化的词中,全局变量的等价物是线程局部变量。

一个可能(也是常见)的用法是,当您有一些不是线程安全的对象,但您希望避免访问该对象(我正在看您,)。相反,为每个线程提供它自己的对象实例

例如:

public class Foo
{
    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}
公共类Foo
{
//SimpleDataFormat不是线程安全的,因此为每个线程提供一个
私有静态最终ThreadLocal格式化程序=新ThreadLocal(){
@凌驾
受保护的SimpleDataFormat初始值()
{
返回新的SimpleDataFormat(“yyyyMMdd HHmm”);
}
};
公共字符串格式(日期)
{
返回格式化程序.get().format(日期);
}
}

.

由于
ThreadLocal
是对给定
线程中数据的引用,因此在使用线程池的应用程序服务器中使用
ThreadLocal
时,可能会导致类加载泄漏。在使用
ThreadLocal
remove()
方法清理
get()
set()
时,需要非常小心

如果完成后不进行清理,它对作为已部署webapp一部分加载的类的任何引用都将保留在中,并且永远不会被垃圾收集。重新部署/取消部署webapp不会清除每个
线程对webapp类的引用,因为
线程不属于webapp所有。每个后续部署将创建一个永远不会被垃圾收集的类的新实例

由于
java.lang.OutOfMemoryError:PermGen space
的原因,最终会出现内存不足的异常,在谷歌搜索之后,可能只会增加
-XX:MaxPermSize
,而不是修复错误

如果最终遇到这些问题,可以通过使用和/或遵循和来确定哪个线程和类保留这些引用


更新:重新发现它帮助我跟踪了我遇到的一些
ThreadLocal
问题。

基本上,当您需要一个变量的值依赖于当前线程时,不方便您以其他方式将该值附加到线程上(例如,子类化线程)

典型的情况是其他框架创建了代码运行的线程,例如servlet容器,或者使用ThreadLocal更有意义,因为此时变量处于“逻辑位置”(而不是挂在线程子类或其他哈希映射中的变量)

在我的网站上,我有一些可能也感兴趣的进一步信息


有些人主张使用ThreadLocal作为一种方式,在某些需要线程编号的并发算法中为每个线程附加一个“线程ID”(参见Herlihy&Shavit)。在这种情况下,检查你是否真的得到了好处

正如@unknown(google)所提到的,它的用法是定义一个全局变量,其中引用的值在每个线程中都是唯一的。它的用法通常需要存储与当前执行线程链接的某种上下文信息

我们在JavaEE环境中使用它将用户标识传递给不支持JavaEE的类(不能访问HttpSession或EJB SessionContext)。通过这种方式,代码可以从任何地方访问标识,而无需在每个方法调用中显式传递标识,它将标识用于基于安全性的操作


大多数Java EE调用中的请求/响应操作周期使这种类型的使用变得简单,因为它提供了定义良好的入口和出口点来设置和取消设置ThreadLocal。

许多框架使用ThreadLocals来维护与当前线程相关的一些上下文。例如,当当前事务存储在ThreadLocal中时,不需要在每次方法调用中都将其作为参数传递,以防堆栈中的某个人需要访问它。Web应用程序可能将有关当前请求和会话的信息存储在ThreadLocal中,以便应用程序能够轻松访问它们。有了Guice,您可以在为注入的对象实现时使用ThreadLocals(Guice的默认设置很可能也使用它们)


ThreadLocals是一种全局变量(尽管由于它们仅限于一个线程,所以不那么糟糕),因此在使用它们时应小心,以避免不必要的副作用和内存泄漏。设计API,使ThreadLocal值在不再需要时始终自动清除,并且不可能错误使用API(例如)。ThreadLocals可用于使代码更干净,在某些罕见的情况下,它们是使某些东西工作的唯一方法(我当前的项目有两种这样的情况;它们记录在“静态字段和全局变量”下)。

您必须非常小心使用ThreadLocal模式
// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);

    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId =
        ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});

    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
        return threadId.get();
    }
}
public class ThreadLocalDemo1 implements Runnable {
    // threadlocal variable is created
    private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue(){
            System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
            return new SimpleDateFormat("dd/MM/yyyy");
        }
    };

    public static void main(String[] args) {
        ThreadLocalDemo1 td = new ThreadLocalDemo1();
        // Two threads are created
        Thread t1 = new Thread(td, "Thread-1");
        Thread t2 = new Thread(td, "Thread-2");
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
        System.out.println("Thread run execution started for " + Thread.currentThread().getName());
        System.out.println("Date formatter pattern is  " + dateFormat.get().toPattern());
        System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
    } 

}
Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
class SimpleDateFormatInstancePerThread {

    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {

        @Override
        protected SimpleDateFormat initialValue() {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
                UUID id = UUID.randomUUID();
                @Override
                public String toString() {
                    return id.toString();
                };
            };
            System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
            return dateFormat;
        }
    };

    /*
     * Every time there is a call for DateFormat, ThreadLocal will return calling
     * Thread's copy of SimpleDateFormat
     */
    public static DateFormat getDateFormatter() {
        return dateFormatHolder.get();
    }

    public static void cleanup() {
        dateFormatHolder.remove();
    }
}
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;


public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);

// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());


// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
    return threadId.get();
}

public static void main(String[] args) {

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

}
}
private static ThreadLocal<Connection> connectionHolder
           = new ThreadLocal<Connection>() {
      public Connection initialValue() {
           return DriverManager.getConnection(DB_URL);
          }
     };

public static Connection getConnection() {
      return connectionHolder.get();
} 
ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
ThreadLocal<String> local = new ThreadLocal<String>(){
    @Override
    protected String initialValue() {
        return "init value";
    }
};
class NotThreadSafe {
    // no parameters
    public NotThreadSafe(){}
}

ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
public class Book implements Runnable {
    private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);

    private final String bookName; // It is also the thread's name
    private final List<String> words;


    public Book(String bookName, List<String> words) {
        this.bookName = bookName;
        this.words = Collections.unmodifiableList(words);
    }

    public void run() {
        WORDS.get().addAll(words);
        System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
        Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
        t1.start();
        t2.start();
    }
}