ThreadLocal的空指针异常问题

在进行get()之前,必须先进行set(),否则可能会报空指针异常

public class ThreadLocalNPE {

    ThreadLocal threadLocal = new ThreadLocal();

    public void set(){
        threadLocal.set(Thread.currentThread().getId());
    }

    /**
     * 这里的返回值使用long的时候,如果没有set()就调用get()那么会报空指针异常,因为牵涉到拆箱转换(将对象类型转换为基本类型)
     * @return
     */
    public Long get(){
        return threadLocal.get();
    }

    public static void main(String[] args) {
        ThreadLocalNPE threadLocalNPE = new ThreadLocalNPE();
        System.out.println(threadLocalNPE.get());
        new Thread(() -> {
            threadLocalNPE.set();
            long threadId = threadLocalNPE.get();
            System.out.println(threadId);
        }).start();

    }
}

ThreadLocal拆装箱引起的空指针异常

public class SpringbootApplication {

    /**
     * 登录方法
     *
     * @param userId
     */
    public void login(Integer userId) {
        UserInfoHolder.userInfoHolder.set(userId);
        System.out.println("用户登录了," + "保存用户id:" + userId);
        order();
    }

    /**
     * 下订单方法
     */
    public void order() {
        System.out.println("订单服务获取了用户id:" + UserInfoHolder.userInfoHolder.get() + ",下了订单");
        logistics();
    }

    /**
     * 物流方法
     */
    public void logistics() {
        UserInfoHolder.userInfoHolder.remove();
        System.out.println("物流服务获取了用户id:" + UserInfoHolder.get() + ",发出了物流");
    }

    /**
     * ThreadLocal
     */
    static class UserInfoHolder {
        private static ThreadLocal userInfoHolder = new ThreadLocal<>();

        /**
         * 自定义了一个get方法,返回int类型
         *
         * @return
         */
        public static int get() {
            return userInfoHolder.get();
        }
    }


    public static void main(String[] args) throws Exception {
        new SpringbootApplication().login(666);
    }
}

报了空指针异常

看下原因:

以下是userInfoHolder.get()的get方法的源码:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

 protected T initialValue() {
        return null;
    }

可以发现它返回的是一个泛型T,而这个T的类型是在ThreadLocal初始化就定义好的了,同时如果没有做任何初始化赋值,或者设值操作,默认就会返回null

以下ThreadLocal类的定义:

public class ThreadLocal {
}

而上述测试代码中,初始化ThreadLocal时,定义的类型是Integer,所以get方法返回的也是Integer类型,由于调用了ThreadLocal的remove操作,所以默认会返回null,照理说返回null也没什么问题,关键就是方法里返回参数用的是int类型做接收,这样就会自动拆装箱,而由于值为null,自然就引起了空指针异常。返回值改为Integ返回er就ok了

 

你可能感兴趣的:(ThreadLocal的空指针异常问题)