Java线程梳理

线程和进程的基本概念

线程:一言以蔽之就是CPU调度的最小单位,必须依赖于进程。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享。

进程:一言以蔽之就是程序运行资源分配的最小单位,当我们运行一个程序,就启动了一 个进程。显然,程序是死的、静态的,进程是活的、动态的。进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,用户进程就是所有由我们启动的进程。

CPU核心数和线程的关系

一般情况下是1:1,但是intel引入超线程之后,可以做到1:2
如图:
在这里插入图片描述

线程的启动

  1. X extends Thread;,然后 X.start
  2. X implements Runnable;然后交给 Thread 运行

Thread 才是 Java 里对线程的唯一抽象,Runnable 只是对任务(业务逻辑) 的抽象。Thread 可以接受任意一个 Runnable 的实例并执行

深入理解start()和run()

Thread类是Java里对线程概念的抽象,可以这样理解:我们通过new Thread() 其实只是 new 出一个 Thread 的实例,还没有操作系统中真正的线程挂起钩来。 只有执行了 start()方法后,才实现了真正意义上的启动线程。 start()方法让一个线程进入就绪队列等待分配 cpu,分到 cpu 后才调用实现 的 run()方法,start()方法不能重复调用,如果重复调用会抛出异常。 而 run 方法是业务逻辑实现的地方,本质上和任意一个类的任意一个成员方 法并没有任何区别,可以重复执行,也可以被单独调用。

线程的状态

在这里插入图片描述

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  3. 阻塞(BLOCKED):表示线程阻塞于锁。
  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
  6. 终止(TERMINATED):表示该线程已经执行完毕

Java中的四种引用

强引用

Object obj=new Object()
只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象实例。

软引用

用来描述有用,但是非必须的对象,被其关联的对象会被标记,待下次GC的时候很可能会被回收。

弱引用

比软引用更弱,甚至都不会被标记,而是下次GC来直接回收。比较常用

虚引用

不常用,我也不太理解。

ThreadLocal使用及其带来的内存泄漏


	static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };

    /**
     *类说明:测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,
     * 看看线程之间是否会互相影响
     */
    public static class TestThread implements Runnable{
        int id;
        public TestThread(int id){
            this.id = id;
        }
        public void run() {
            System.out.println(Thread.currentThread().getName()+":start");
            Integer s = threadLocal.get();
            s = s+id;
            threadLocal.set(s);
            System.out.println(Thread.currentThread().getName()+" :"
                    +threadLocal.get());
            //threadLocal.remove();
        }
    }

每个 Thread 维护一个 ThreadLocalMap,这个映射表的 key 是 ThreadLocal 实例本身,value 是真正需要存储的 Object,也就是说 ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value。仔细观察ThreadLocalMap,这个 map 是使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。
在这里插入图片描述

在这里插入图片描述
这样,当把 threadlocal 变量置为null 以后,没有任何强引用指向 threadlocal 实例,所以 threadlocal 将会被 gc 回收。这样一来,ThreadLocalMap 中就会出现 key 为 null 的 Entry,就没有办法访问这些 key 为 null 的 Entry 的 value,如果当前线程再迟迟不结束的话,这些 key 为 null 的 Entry 的 value 就会一直存在一条强 引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,而这块 value 永 远不会被访问到了,所以存在着内存泄露。
可以通过调用threadLocal.remove()解决。
那么问题来了?
既然弱引用的ThreadLocal会出现内存泄漏,为啥还要WeakReference<ThreadLocal<?>>,直接强引用不香么??

key 如果使用强引用:引用 ThreadLocal 的对象被回收了,但是 ThreadLocalMap 还持有 ThreadLocal 的强引用,如果没有手动删除,ThreadLocal 的对象实例不会 被回收,导致 Entry 内存泄漏

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alex_ChuTT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值