为了账号安全,请及时绑定邮箱和手机立即绑定

线程的细节

标签:
Java

在项目中遇到一个问题,有同事找我,他引用了一个sdk结果导致了进程无法正常退出。这个场景很简单,主要是使用了sdk里启动了线程,但是不是daemon的。最后是sdk的同学修改了线程状态,把线程启动都设置了daemon。但是有一次,我自己测试的时候用旧的sdk,发现并没有阻塞进程退出。于是检测了一下区别,发现自己是用线程池调用的sdk的内容。我自己给线程池设置了线程工厂。工厂里把线程池的线程设置成了daemon。但是我没有设置给sdk启动的线程。于是有了一个疑问。

线程的构造

为了解决上面的疑问,我们看看线程的构造函数

		this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

发现在构建线程的方法里,从父线程里获取了一堆属性。其中就有daemon。也就是说父线程如果是daemon的,那么创建出来的线程也是daemon的。父线程是非daemon的,子线程也是非daemon的。我们平时创建的都是从main线程开始的,main是非daemon的,所以默认需要我们手动设置,当我们设置好一个之后,用那个线程再进行一些操作的时候,就都是daemon的。

inheritableThreadLocals

在上面的代码中,我们还能看到inheritableThreadLocals这个变量。他本身是用来把父线程的熟悉,都给子线程。

    private static InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal<>();
    private static ThreadLocal threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {


        threadLocal.set("threadLocal");
        inheritableThreadLocal.set("inheritableThreadLocal");
        new Thread(()->{

            System.out.println(  threadLocal.get());
            System.out.println(inheritableThreadLocal.get());
        }).start();

    }

这里要区分ThreadLocal和InheritableThreadLocal。前者是线程私有的,后者是父子线程共享的。
这里也有很多人想到,如果只是在new子线程的时候,直接把ThreadLocal的值赋值给子线程的值是不是也可以呢?
这个其实做快照是可以的。例如traceid这种,一旦产生就不会变化,没有可变性,当然可以在new线程的时候进行直接赋值。
InheritableThreadLocal本身也是不可变的。子线程改变后,子线程创建的线程会收到变化,但是父线程是看不到的。

 private static InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal<>();
    private static ThreadLocal threadLocal = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {


        threadLocal.set("threadLocal");
        inheritableThreadLocal.set("inheritableThreadLocal");
        new Thread(() -> {

            System.out.println(threadLocal.get());
            System.out.println(inheritableThreadLocal.get());
            inheritableThreadLocal.set("inheritableThreadLocal2");
            new Thread(()->{
                System.out.println(inheritableThreadLocal.get());
            }).start();

        }).start();

        Thread.sleep(3000);
        System.out.println(inheritableThreadLocal.get());
    }

最终执行的效果是

null
inheritableThreadLocal
inheritableThreadLocal2
inheritableThreadLocal

这种用起来就和threadlocal一样了,都是快照,那能不能变化呢。
可以通过对象来进行修改,他只是传递的引用不能改变了。

  static class Test{
        int i;
    }
    private static InheritableThreadLocal inheritableObject = new InheritableThreadLocal<>();
    public static void main(String[] args) throws InterruptedException {
        Test test=new Test();
        test.i=6;
        inheritableObject.set(test);
        new Thread(() -> {


            System.out.println(inheritableObject.get().i);
            inheritableObject.get().i=1;

        }).start();
        Thread.sleep(3000);
        System.out.println(inheritableObject.get().i);
    }

这种情况下,子线程的修改,就可以再父线程上看到了。
但是ThreadLocal就不行了。

    private static ThreadLocal threadObject = new ThreadLocal<>();
    public static void main(String[] args) throws InterruptedException {
        Test test=new Test();
        test.i=6;
        threadObject.set(test);
        new Thread(() -> {


            System.out.println(threadObject.get().i);
            threadObject.get().i=1;

        }).start();
        Thread.sleep(3000);
        System.out.println(threadObject.get().i);
    }

这种情况下ThreadLocal本身就是空,子线程无法看到值,会有空指针异常。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1.6万
获赞与收藏
380

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消