栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

线程的细节

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程的细节

在项目中遇到一个问题,有同事找我,他引用了一个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本身就是空,子线程无法看到值,会有空指针异常。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号