Thread源码分析-java8

本文深入分析了Java 8中Thread的特性,包括守护线程、线程创建、线程状态、线程优先级以及Thread类的重要方法如setDaemon、setName、yield和join等。同时,讨论了ThreadLocalMap的实现和线程安全问题,最后简要介绍了源码分析的关键点。
摘要由CSDN通过智能技术生成
1.Thread特性分析
  • 守护线程Daemon
    • 定性:支持性线程,主要用于程序中后台调度以及支持性工作。
    • 当JVM中不存在Daemon线程时,JVM将会退出。
    • 将一个线程设定为Daemon的方法:
      • 调用Thread.setDaemon(true)。
      • Daemon属性的设定只能在启动线程前设置,启动线程后不能设置。
    • JVM退出时Daemon线程中的finally块中的代码不一定会执行。因此不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。
    • 当JVM启动时,通常会有唯一的一个非守护线程(这一线程用于调用指定类的main()方法).
  • 2种方式创建一个可执行线程
    • 定义一个继承Thread类的子类.子类可覆写父类的run()方法.
    • 使类实现Runnable接口
  • 线程名字
    • 每一个线程都有一个用于目的标识的名字
    • 多个线程可以有相同的名字
    • 线程名类型为volatile类型,可更改且线程可见
  • 线程ID
    • 一个long类型的正数,在线程被创建时就有
    • 在其生命周期内都不会更改且独一无二
    • 当线程终止时,则此线程的ID会被重复使用
  • 线程优先级
    • 默认优先级:5
    • 最高优先级:10
    • 最低优先级:1
  • 线程状态6种
    • NEW:线程还未开始,只是进行了一些线程创建的初始化操作,但未调用start()方法.
    • RUNNABLE:线程在JVM里面处于运行状态(这里就绪和运行同属于运行).
    • BLOCKED:线程正在等待一个监视器锁,处于阻塞状态.
    • WAITING:一个线程在等待另一个线程的特定操作(通知or中断),这种等待是无限期的.
    • TIMED_WAITING:一个线程在等待另一个线程的特定操作,这种等待是有时间限制的.一旦超时则线程自行返回.
    • TERMINATED:线程已退出.表示线程已经执行完毕.
  • 浅拷贝
    • 不支持
    • 替代操作: 构造一个新的线程。
2. 类、方法、字段分析
  • 实现接口和继承类
    只实现了一个接口Runnable
  • threadLocals变量
    • 类型为:ThreadLocal.ThreadLocalMap
    • 功能:此线程的本地变量值.此map由ThreadLocal类进行维护,因为这个类在ThreadLocal中是包级私有的.
    • ThreadLocalMap:一个用于维护线程本地变量的hashmap,此hashmap的key引用类型为弱引用,这是为了支持大且长期存活的使用方法.
  • inheritableThreadLocals变量
    • 类型:ThreadLocal.ThreadLocalMap
    • 功能:和此线程相关的由继承得到的本地变量值
  • public static native void yield()方法
    • 功能:
      提示线程调度器当前线程愿意放弃当前CPU的使用。当然调度器可以忽略这个提示
    • 设计目的:
      让出CPU是一种启发式的尝试,以改善线程之间的相对进展,否则将过度利用CPU。
    • 使用场景:
      此方法很少有适用的场景.它可以用用于debug或者test,通过跟踪条件可以重现bug
  • public final synchronized void setName(String name)方法
    • 功能
      设定线程名
    • 注意这是一个同步方法
  • 3个join方法
    • 功能:等待直到线程终止
    • public final synchronized void join(long millis)
      • 最多等待参数millis(ms)时长当前线程就会死亡.参数为0时则要持续等待
      • 此方法为同步方法
    • public final synchronized void join(long millis, int nanos)
      • 等待时间单位为纳秒,其它解释都和上面方法一样
      • 此方法为同步方法
    • public final void join() throws InterruptedException
  • toString()格式
    线程名+优先级+所属组别
  • @CallerSensitive注解
    • 功能:
      这个注解是为了堵住漏洞用的
    • 原理:
      曾经有黑客通过构造双重反射来提升权限,原理是当时反射只检查固定深度的调用者的类,看它有没有特权.使用CallerSensitive后,getCallerClass不再用固定深度去寻找actual caller(“我”),而是把所有跟反射相关的接口方法都标注上CallerSensitive,搜索时凡看到该注解都直接跳过,这样就有效解决了这类的黑客问题.
3.源码分析
package sourcecode.analysis;

/**
 * @Author: cxh
 * @CreateTime: 18/5/11 19:11
 * @ProjectName: JavaBaseTest
 */
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.LockSupport;
import sun.nio.ch.Interruptible;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.security.util.SecurityConstants;


/**
 * 线程就是程序中一个线程的执行.JVM允许一个应用中多个线程并发执行.
 *
 * 每个线程都有优先级.高优先级线程优先于低优先级线程执行.
 * 每个线程都可以(不可以)被标记为守护线程.
 * 当线程中的run()方法代码里面又创建了一个新的线程对象时,新创建的线程优先级和父线程优先级一样.
 * 当且仅当父线程为守护线程时,新创建的线程才会是守护线程.
 *
 * 当JVM启动时,通常会有唯一的一个非守护线程(这一线程用于调用指定类的main()方法)
 * JVM会持续执行线程直到下面情况某一个发生为止:
 * 1.类运行时exit()方法被调用 且 安全机制允许此exit()方法的调用.
 * 2.所有非守护类型的线程均已经终止,or run()方法调用返回  or 在run()方法外部抛出了一些可传播性的异常.
 *
 *
 * 有2种方式可以创建一个可执行线程.
 * 1.定义一个继承Thread类的子类.子类可覆写父类的run()方法.子类实例分配内存后可运行(非立即,取决于CPU调用)
 * 比如:计算大于指定值的素数的线程可以写成如下
 * class PrimeThread extends Thread {
 *         long minPrime;
 *         PrimeThread(long minPrime) {
 *             this.minPrime = minPrime;
 *         }
 *
 *         public void run() {
 *             // compute primes larger than minPrime
 *              . . .
 *         }
 * }
 * 下面的代码将创建一个线程并启动它.
 * PrimeThread p = new PrimeThread(143);
 *     p.start();
 *
 * 2.另一个实现线程的方式就是使类实现Runnable接口.
 * 此类自己会实现run()方法.然后此线程会被分配内存,当线程被创建时,会传入一个参数,然后开始执行.
 * 此种方式的样例代码如下:
 *
 * class PrimeRun implements Runnable {
 *         long minPrime;
 *         PrimeRun(long minPrime) {
 *             this.minPrime = minPrime;
 *         }
 *
 *         public void run() {
 *             // compute primes larger than minPrime
 *              . . .
 *         }
 * }
 * 下面的代码能够创建一个线程并开始执行:
 *  PrimeRun p = new PrimeRun(143);
 *     new Thread(p).start();
 *
 * 每一个线程都有一个用于目的标识的名字.多个线程可以有相同的名字.
 * 线程被创建时如果名字没有被指定,则系统为其自动生成一个新的名字.
 *
 * 除非特别说明,否则在创建线程时传入一个null参数到构造器或者方法会抛出空指针异常NullPointerException
 *
 * @author  unascribed
 * @see     Runnable
 * @see     Runtime#exit(int)
 * @see     #run()
 * @see     #stop()
 * @since   JDK1.0
 */
public class Thread implements Runnable {

    //确保本地注册(类构造器方法<clinit>方法用于类初始化)是创建一个线程首要做的事情.
    private static native void registerNatives();//注册的都是一些本地方法
    static {
        registerNatives();
    }

    private volatile String name;//线程名:可更改且线程可见
    private int            priority;//线程优先级用一个int数字表示
    private Thread         threadQ;//
    private long           eetop;


   //是否单步执行此线程
    private boolean     single_step;

    //此线程是否为守护线程
    private boolean     daemon = false;

    //JVM状态
    private boolean     stillborn = false;

    //run方法执行的目标代码
    private Runnable target;

    //此线程所属的组别
    private ThreadGroup group;

    //此类型的类加载器
    private ClassLoader contextClassLoader;

    //此线程继承的访问控制上下文
    private AccessControlContext inheritedAccessControlContext;

    //用于自动编号的匿名线程
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    //此线程的本地变量值.此map由ThreadLocal类进行维护,因为这个类在ThreadLocal中是包级私有的.
    //ThreadLocalMap是一个用于维护线程本地变量的hashmap,此hashmap的key引用类型为弱引用,这是为了支持大且长期存活的使用方法.
    ThreadLocal.ThreadLocalMap threadLocals = null;

    //和此线程相关的由继承得到的本地变量值.
    //此hashmap由InheritableThreadLocal类进行维护.
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    /*
     * 此线程请求栈的深度,如果线程创建者未指定栈深度则其值为0.
     * 此数字如何被使用完全取决于虚拟机自己;也有一些虚拟机会忽略此变量值.
     */
    private long stackSize;

    //此变量表示:在本地线程终止后,JVM私有的一个状态值
    private long nativeParkEventPointer;

    //线程id
    private long tid;

    //用于生成线程ID
    private static long threadSeqNumber;

    //为工具提供的线程状态值,初始化值表示当前线程还未运行
    private volatile int threadStatus = 0;


    //私有同步方法,获取下一个线程id
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

    /**
     * 此变量为用于调用java.util.concurrent.locks.LockSupport.park方法的参数.
     * 其值由方法(private) java.util.concurrent.locks.LockSupport.setBlocker进行设定.
     * 其值访问由方法java.util.concurrent.locks.LockSupport.getBlocker进行获取.
   
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值