ThreadLocal

当一个类只需要在全局只需要一份实例存在,可以使用单例模式实例该类,比如mybatis的错误日志打印类ErrorContext,现在利用饿汉模式实现一个单例类,这个类保证了程序每个地方拿到的实例都是同一个。

public class ThreadLocalLearn {
    private static ThreadLocalLearn threadLocalLearn = new ThreadLocalLearn();
    private ThreadLocalLearn() {
        // 单例模式
    }
    public static ThreadLocalLearn instance() {
        return threadLocalLearn;
    }
}

这个类目前是线程安全的,但是当这个类含有私有属性,用来记录最终打印的内容时,这样的实现是线程不安全的。

public class ThreadLocalLearn {
    // 线程不安全的单例模式
    private static ThreadLocalLearn threadLocalLearn = new ThreadLocalLearn();

    private String name = null;
    private Integer age = null;

    private ThreadLocalLearn() {
        // 单例模式
    }

    public static ThreadLocalLearn unSafeInstance() {
        return threadLocalLearn;
    }
}

一个线程设置name时,另一个线程可能同时设置了age,导致name和age并非同一个线程赋的值。

而为了一个类在多线程中针对每个线程的单例模式实现,一种设计方法需要用到ThreadLocal,简要的来说,每个线程都有一个独立的对象区,而ThreadLocal就是给线程设置或者获取线程对象的方法。

public class ThreadLocalLearn {
    // 线程安全的单例模式
    private static final ThreadLocal<ThreadLocalLearn> LOCAL = new ThreadLocal<>();

    private String name = null;
    private Integer age = null;

    private ThreadLocalLearn() {
        // 单例模式
    }

    public static ThreadLocalLearn safeInstance() {
        ThreadLocalLearn threadLocalLearn = LOCAL.get();
        if (threadLocalLearn == null) {
            threadLocalLearn = new ThreadLocalLearn();
            LOCAL.set(threadLocalLearn);
        }
        return threadLocalLearn;
    }
}

当ThreadLoal调用set方法时,会将实例出的ThreadLocalLearn对象绑定到当前线程上,get方法时会从当前线程获取这个对象,从而达到针对每个线程的单例作用,由于每个线程获取到的ThreadLocalLearn都是自身线程创建的,也就不存在name,age设置冲突问题。

ThreadLocal的具体实现是:

  1.对每个Thread,都维护了一个Map(ThreadLocal.ThreadLocalMap),用于绑定ThreadLocal Set的实例集合

  2.ThreadLocal 调用Get方法时,首先获取当前线程(Thread.currentThread()),再获取ThreadLocalMap,像使用HashMap一样Get出对象(Key为ThreadLocal<T> 中的T, 比如ThreadLocalLearn)

  3. Set 与(2)类型,实现非常简单。

------------------------------------------------------------------------------------------------------------------------------------------------------------------

完整例子代码:

public class ThreadLocalLearn {
    // 线程不安全的单例模式
    private static ThreadLocalLearn threadLocalLearn = new ThreadLocalLearn();
    // 线程安全的单例模式
    private static final ThreadLocal<ThreadLocalLearn> LOCAL = new ThreadLocal<>();

    private String name = null;
    private Integer age = null;

    private ThreadLocalLearn() {
        // 单例模式
    }

    public static ThreadLocalLearn unSafeInstance() {
        return threadLocalLearn;
    }

    public static ThreadLocalLearn safeInstance() {
        ThreadLocalLearn threadLocalLearn = LOCAL.get();
        if (threadLocalLearn == null) {
            threadLocalLearn = new ThreadLocalLearn();
            LOCAL.set(threadLocalLearn);
        }
        return threadLocalLearn;
    }

    public ThreadLocalLearn personName(String personName) {
        this.name = personName;
        return this;
    }

    public ThreadLocalLearn personAge(Integer personAge) {
        this.age = personAge;
        return this;
    }

    @Override
    public String toString() {
        StringBuilder description = new StringBuilder();
        if (name != null) {
            description.append("|Name: ");
            description.append(this.name);
        }

        if (age != null) {
            description.append("|Age: ");
            description.append(this.age);
        }

        return description.toString();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            // 线程不安全例子
            // Thread thread = new Thread(new UnsafeTester(i));
            // 线程安全例子
            Thread thread = new Thread(new SafeTester(i));
            thread.start();
        }
    }
}


class UnsafeTester implements Runnable {
    private Integer index;

    public UnsafeTester(Integer index) {
        this.index = index;
    }

    @Override
    public void run() {
        ThreadLocalLearn threadLocalLearn = ThreadLocalLearn.unSafeInstance();
        threadLocalLearn.personAge(index).personName(String.valueOf(index));
        System.out.println(threadLocalLearn.toString());
    }

}


class SafeTester implements Runnable {
    private Integer index;

    public SafeTester(Integer index) {
        this.index = index;
    }

    @Override
    public void run() {
        ThreadLocalLearn threadLocalLearn = ThreadLocalLearn.safeInstance();
        threadLocalLearn.personAge(index).personName(String.valueOf(index));
        System.out.println(threadLocalLearn.toString());
    }
}

参照:

  1. mybatis3 ErrorContext 实现。

原文地址:https://www.cnblogs.com/syui-terra/p/10489529.html