ThreadLocal作用实例

ThreadLocal

ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的,它提供了一种方法来创建只能被当前线程访问的变量。这意味着如果有两个不同的线程访问同一个ThreadLocal变量,那么这两个线程将不能看到彼此的值,这就是“线程本地”的含义。

ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递);
使用ThreadLocal要用try … finally结构,并在finally中清除。

首先看一个线程不安全的实例

    private static SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        while (true) {
            new Thread(() -> {
                String dateStr = f.format(new Date());
                try {
                    Date parseDate = f.parse(dateStr);
                    String dateStrCheck = f.format(parseDate);
                    boolean equals = dateStr.equals(dateStrCheck);
                    if (!equals) {
                        System.out.println(equals + " " + dateStr + " " + dateStrCheck);
                    } else {
                        System.out.println(equals);
                    }
                } catch (ParseException e) {
                    System.out.println(e.getMessage());
                }
            }).start();
        }
    }

在代码中每个线程都会 对f进行操作,会导致f这个对象发生变化,由于f这个对象是共享变量,各个线程之间互相影响,由此产生了三种情况
输出结果如下,正常情况为true,如果两个线程分别修改了数据,那么可能是false,当然,由于还处在中间过程,还会导致异常情况,那么该如何解决这个问题呢,就需要ThreadLocal了。

true
true
false 2024-01-31 10:09:52 0000-01-31 10:09:52
false 2024-01-31 10:09:52 0052-01-31 10:09:52
true
true
Exception in thread "Thread-1766" java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Long.parseLong(Long.java:601)
	at java.lang.Long.parseLong(Long.java:631)
	at java.text.DigitList.getLong(DigitList.java:195)
	at java.text.DecimalFormat.parse(DecimalFormat.java:2084)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at shujujiegou.testThreadLocal.lambda$main$0(testThreadLocal.java:16)
	at java.lang.Thread.run(Thread.java:748)

现在已经知道ThreadLocal是本地变量的意思,相当于把共享变量与本地进行了隔离,每个线程复制一份,因此在操作过程中互不干扰
写法如下:

private static ThreadLocal threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static void main(String[] args) {
    while (true) {
        new Thread(() -> {
            String dateStr = threadLocal.get().format(new Date());
            try {
                Date parseDate = threadLocal.get().parse(dateStr);
                String dateStrCheck = threadLocal.get().format(parseDate);
                boolean equals = dateStr.equals(dateStrCheck);
                if (!equals) {
                    System.out.println(equals + " " + dateStr + " " + dateStrCheck);
                } else {
                    System.out.println(equals);
                }
            } catch (ParseException e) {
                System.out.println(e.getMessage());
            }
        }).start();
    }
}
 

ThreadLocal数据结构
1、它是一个数组结构。
3、Entry,这里没用再打开,其实它是一个弱引用实现,static class Entry extends WeakReference>。这说明只要没用强引用存在,发生GC时就会被垃圾回收。
3、数据元素采用哈希散列方式进行存储,不过这里的散列使用的是 斐波那契(Fibonacci)散列法,后面会具体分析。
4、另外由于这里不同于HashMap的数据结构,发生哈希碰撞不会存成链表或红黑树,而是使用开放寻址进行存储。也就是同一个下标位置发生冲突时,则+1向后寻址,直到找到空位置或垃圾回收位置进行存储。
6、大于2/3就会扩容
7、探测式清理,其实这也是非常耗时。为此我们在使用 ThreadLocal 一定要记得 new ThreadLocal<>().remove(); 操作。避免弱引用发生GC后,导致内存泄漏的问题。

你可能感兴趣的:(Java基础与八股,java,数据结构,jvm)