并发编程(八)Thread源码分析,方法示例

本文深入解析Java中的Thread类,涵盖线程状态、线程生命周期、常用方法如start、run、sleep、interrupt等,并通过实例演示线程的创建与控制,同时探讨并发编程中的线程管理与上下文切换概念。

1、Thread类是什么

Java中Thread类用来实现线程编程

什么是线程、什么是进程、为什么用到并发可以查看我的并发系列第一篇文章《并发编程(一)为什么用并发编程、JMM内存模型、volatile、内存屏障》

2、线程的状态

new java.lang.Thread().start()只有调用start()方法的时候,才会真正的在 JVM中去创建线程。

线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:

  • 创建:new
  • 运行:runnable(运行中:running、就绪:ready)
  • 等待:waiting(等待超时:timed waiting)
  • 阻塞:blocked
  • 终止:terminated

内部类:线程的状态

public enum State {
    //初始状态,刚被new出来,还没调用start时的状态
    NEW,
    //运行时状态,或者就绪时状态,调用start后的状态
    RUNNABLE,
    //等待获取对象锁时的状态
    BLOCKED,
    //等待被人唤醒状态时状态
    WAITING,
    //等待别人唤醒,但设置了最长等待时间的状态
    TIMED_WAITING,
    //run方法运行结束的状态
    TERMINATED;
}

在这里插入图片描述

并发指的是多个任务交替进行。对于线程的上下文切换实际上就是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。线程切换时同样会带来一定的开销代价

3、Thread类的方法

在这里插入图片描述

用native关键字修饰的函数表明该方法的实现并不是在Java中去完成,而是由C/C++去完成

3.1、静态registerNatives方法:注册的方法

参考博文:java registernatives_Java 之 native 关键字与 registerNatives 方法

/**
* 先定义了registerNatives()方法,然后当该类被加载的时候,调用该方法完成对该类中本地方法的注册。
* 当包含registerNatives()方法的类被加载的时候,注册的方法就是该类所包含的除了registerNatives()方法以外的所有本地方法。
*/
private static native void registerNatives();
static {
    registerNatives();
}

3.2、init方法:初始化方法


    /**
     * Initializes a Thread.
     *
     * @param g 线程组:表示一组线程
     * @param target 调用其run()方法的对象
     * @param name 新线程的名字
     * @param stackSize 新线程所需的堆栈大小,零表示忽略此参数。
     * @param acc 要继承的AccessControlContext,如果为null,则为AccessController.getContext()
     * @param inheritThreadLocals 如果{@code true},则从构造线程 继承 可继承线程局部变量的初始值
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        // 线程名字(指定或默认线程名:"Thread-" + nextThreadNum())
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        // 获取当前调用的线程,把当前调用的线程作为父线程
        java.lang.Thread parent = currentThread();
        // 获取系统安全接口。
        // 如果构造方法传入的线程组为null,则通过这个流程来决定其线程组,通常,新建线程的线程组为其创建线程的线程组
        SecurityManager security = System.getSecurityManager();
        // 新建线程的线程组
        if (g == null) {
            if (security != null) {
                // 如果安全控制器不为null,线程组设置为当前线程的线程组
                g = security.getThreadGroup();
            }

            if (g == null) {
                //如果安全控制器的线程组为null 使用父线程的线程组
                g = parent.getThreadGroup();
            }
        }

        // 检查线程组是否需要访问权限
        g.checkAccess();

        // 检查是否有请求权限
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }
        // 把线程组的unstarted的线程数量加1
        g.addUnstarted();
        // 设置线程组为上面获取的线程组
        this.group = g;
        /* 设置当前线程是否为守护线程,默认是和当前类的ThreadGroup设置相
         *  同。如果是守护线程的话,当前线程结束会随着主线程的退出而退出。
         * jvm退出的标识是,当前系统没有活跃的非守护线程。
         *
         * 把线程的daemon设置为父线程的damon
         */
        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();
        // 设置Runnable对象,为传入的Runnable对象
        this.target = target;
        // 设置线程的优先级为当前对象的优先级
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                    ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        /*设置指定的栈大小,如果未指定大小,将在jvm 初始化参数中声明:Xss参数进行指定*/
        this.stackSize = stackSize;

        /* Set thread ID */
        // 产生新的tid并设置
        tid = nextThreadID();
    }

3.3、setPriority 方法:设置线程的优先级

//设置线程的优先级
public final void setPriority(int newPriority) {
    //当前线程的线程组
    ThreadGroup g;
    //检查访问权限
    checkAccess();
    //参数校验
    if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
        throw new IllegalArgumentException();
    }
    if((g = getThreadGroup()) != null) {
        //如果当前线程组不为null
        if (newPriority > g.getMaxPriority()) {
            //传入的新优先级大于线程组最大的优先级
            //把新的优先级设置为线程组最大的优先级
            newPriority = g.getMaxPriority();
        }
        //设置线程的优先级
        setPriority0(priority = newPriority);
    }
}
//真正的设置线程优先级方法,这个是native方法
private native void setPriority0(int newPriority);

3.4、nextThreadNum 方法:获取下一个线程号,这个是静态同步方法

//获取下一个线程号,这个是静态同步方法
private static synchronized int nextThreadNum() {
    //直接返回threadInitNumber,然后再把threadInitNumber加1
    return threadInitNumber++;
}

3.5、nextThreadID 方法:获取下一个线程id,这是个静态同步方法

//获取下一个线程id,这是个静态同步方法
private static synchronized long nextThreadID() {
    //先把threadSeqNumber加1,再返回
    return ++threadSeqNumber;
}

3.6、yield方法

使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。

public static native void yield();

3.7、sleep 方法:让当前线程休眠多少毫秒

//让当前线程休眠多少毫秒
//当前线程不会让出对象的锁让其他线程执行
public static native void sleep(long millis) throws InterruptedException;

public static void sleep(long millis, int nanos)
    throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }
    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }
    //最终调用的还是上面的方法,只不过把纳秒改成了毫米
    sleep(millis);
}

3.8、clone 方法

//不支持克隆,直接抛出异常
//这种方式要学习,JDK中有很多这种操作。子类不支持父类方法,直接抛出异常
@Override
protected Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
}

3.9、start 方法:开始一个线程,只是把线程的状态从NEW变成了Runnable状态

//开始一个线程
//只是把线程的状态从NEW变成了Runnable状态
//并不一定立即执行,要看能不能抢到cpu的资源
public synchronized void start() {
   if (threadStatus != 0)
       //NEW状态为0
       //如果当前线程不是NEW状态,抛出异常
       throw new IllegalThreadStateException();

   //唤醒线程组,把当前线程加入到线程组的线程数组中去
   //把线程组的线程数量加1,并把线程组的unstarted的线程数量减1
   group.add(this);
   //设置启动状态为false
   boolean started = false;
   try {
       //真正的启动线程方法,这个是一个native方法
       start0();
       //启动线程成功,把启动状态置为true
       started = true;
   } finally {
       try {
           if (!started) {
               //如果启动失败,通知线程组做出相应的操作
               group.threadStartFailed(this);
           }
       } catch (Throwable ignore) {

       }
   }
}
//真正启动线程的方法,是一个native方法
//可以看出启动线程操作是JVM去处理的
private native void start0();

3.10、run 方法:执行具体业务的方法

//执行具体业务的方法,这个应该是JVM底层回调
@Override
public void run() {
    //直接交给Runnable对象去执行
    if (target != null) {
        target.run();
    }
}

3.11、exit 方法:在Thread退出之前,系统调用exit来回收资源

//在Thread退出之前,系统调用exit来回收资源
private void exit() {
    if (group != null) {
        //如果线程组不为null
        //唤醒线程组的其他线程
        group.threadTerminated(this);
        group = null;
    }
    /* Aggressively null out all reference fields: see bug 4006245 */
    target = null;
    /* Speed the release of some of these resources */
    threadLocals = null;
    inheritableThreadLocals = null;
    inheritedAccessControlContext = null;
    blocker = null;
    uncaughtExceptionHandler = null;
}

3.12、stop 方法:强制停止当前线程

//强制停止当前线程
public final void stop() {
   //获取安全管理器
   SecurityManager security = System.getSecurityManager();
   if (security != null) {
       //如果安全管理器不为null
       //检查权限
       checkAccess();
       if (this != Thread.currentThread()) {
           //如果不是当前线程自己调用
           //检查权限
           security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
       }
   }
   if (threadStatus != 0) {
       //如果当前线程的状态不是NEW,可能处于暂停状态
       //调用resume方法恢复一个暂停的线程
       resume(); // Wake up thread if it was suspended; no-op otherwise
   }

   //VM可以操作所有的状态
   //通过stop0来执行
   stop0(new ThreadDeath());
}
//强制停止当前线程,这个是一个native方法
private native void stop0(Object o);

3.13、resume 方法:恢复一个暂停的线程,如果线程没有暂停,不做其他操作

//恢复一个暂停的线程,如果线程没有暂停,不做其他操作
@Deprecated
public final void resume() {
     //检查权限
     checkAccess();
     //调用reume0来恢复暂停的线程
     resume0();
 }
//恢复暂停的线程,这个是一个nateive方法
private native void resume0();

3.14、interrupt 方法:设置线程中断信号

//中断线程
public void interrupt() {
   if (this != Thread.currentThread())
        //如果不是当前线程自己调用
        //检查权限
        checkAccess();
    //同步blockerLock
    //同步代码块里包含blocker,因为blockerLock就是为了同步blocker
    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            //如果blocker不为null,即现在处于I/O阻塞状态
            //仅仅设置interrupt状态
            interrupt0();           
            //设置interrupt status后调用blocker的interrup方法
            b.interrupt(this);
            return;
        }
    }
    //设置interrupt status
    interrupt0();
}
//设置interrupt status 这是一个native方法
private native void interrupt0();

3.15、interrupted 方法:测试调用线程是否处于中断状态

  • 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
  • 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true。被设置中断标志的线程将继续正常运行,不受影响。
  • interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就可以这样做。
    1. 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
    2. 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)

调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。

// 测试当前线程是否处于中断状态
public boolean isInterrupted() {
    //直接调用isInterrupted方法
    return isInterrupted(false);
}
//测试线程是否中断,这是一个native方法
//ClearInterrupted true 会重置interrupted state, false 不会重置interrupted state
 private native boolean isInterrupted(boolean ClearInterrupted);

3.16、suspend 方法:暂停线程

//暂停线程
@Deprecated
    public final void suspend() {
        //检查权限
        checkAccess();
        //调用susupend0来暂停线程
        suspend0();
    }
//暂停线程,这是一个native方法
private native void suspend0();

3.17、setName 方法:设置线程名称,这是一个同步方法

//设置线程名称,这是一个同步方法
public final synchronized void setName(String name) {
        //检查权限
        checkAccess();
        //参数检查
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        //为name属性赋值
        this.name = name;
        if (threadStatus != 0) {
            //如果当前线程不是NEW状态,调用setNativeName方法
            setNativeName(name);
        }
    }
//设置线程名称
private native void setNativeName(String name);

3.18、join 方法:等待这个线程结束后再执行

//等待这个线程结束后再执行
public final void join() throws InterruptedException {
   //调用下面的方法
    join(0);
}
//等待指定最大时间或者这个线程执行结束再执行
public final synchronized void join(long millis, int nanos)
    throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }
    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }
    //上面是把纳秒转换为毫秒
    //调用下面的方法
    join(millis);
}
//这个才是真正的join方法
//等待指定毫秒数,或者当前线程执行完毕后再截止执行调用线程
public final synchronized void join(long millis)
    throws InterruptedException {
   long base = System.currentTimeMillis();
    long now = 0;
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (millis == 0) {
        //不指定等待时间的情况
        //一直while循环,直到当前线程结束再执行调用线程
        while (isAlive()) {
            wait(0);
        }
    } else {
        //指定等待时间的情况
        //while循环判断当前线程是否结束
        while (isAlive()) {
            long delay = millis - now;
            //如果等待时间超过指定时间退出,继续执行调用线程
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

3.19、setDaemon 方法:设置线程是守护线程还是用户线程

//设置线程是守护线程还是用户线程
//必须在start之前调用
public final void setDaemon(boolean on) {
    //检查权限
    checkAccess();
    //如果线程在运行抛出异常
    if (isAlive()) {
        throw new IllegalThreadStateException();
    }
    //设置线程标记
    daemon = on;
}

4、示例

4.0、三种实现线程的方式:继承Thear、实现Runnable、实现Callable(有返回值)

/**
 * 三种实现线程的方式
 */
public class DemoExecutor  extends Thread {
    public DemoExecutor(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println("Thread继承方式运行");
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        DemoExecutor executor1 = new DemoExecutor("张三");
        executor1.start();

        Thread thread = new Thread(new RunnableDemo(),"李四");
        thread.start();

        // 有返回方式运行
        CallableDemo callableDemo = new CallableDemo();
        FutureTask<Integer> callableTask = new FutureTask<>(callableDemo);
        Thread thread1 = new Thread(callableTask);
        thread1.start();

        System.out.println("callableTask="+callableTask.get());

    }


    /**
     * Runnable方式实现线程,不可抛出异常
     */
    static class RunnableDemo implements Runnable{

        @Override
        public void run() {
            System.out.println("Runnable实现方式运行");
        }
    }

    /**
     * 多线程返回值,可抛异常
     */
    static class CallableDemo implements Callable<Integer>{

        @Override
        public Integer call() {
            System.out.println("Callable实现方式运行");
            return 1;
        }
    }
}

4.1、run、yield、start

public class DemoExecutor  extends Thread {
    public DemoExecutor(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 1; i <= 50; i++) {
            System.out.println("" + this.getName() + "-----" + i);
            // 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
            if (i == 30) {
            	// 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)
                this.yield();
            }
        }

    }

    public static void main(String[] args) {
        DemoExecutor executor1 = new DemoExecutor("张三");
        DemoExecutor executor2 = new DemoExecutor("李四");
        executor1.start();
        executor2.start();
    }
}

在这里插入图片描述

4.2、isAlive、join、sleep


public class DemoExecutor  extends Thread {
    public DemoExecutor(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 1; i <= 150; i++) {
            if(i == 150){
                System.out.println("" + this.getName() + "-----" + i);
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {
        DemoExecutor executor1 = new DemoExecutor("张三");
        DemoExecutor executor2 = new DemoExecutor("李四");
        executor1.start();

        executor2.start();

        // 判断线程是否活动
        boolean alive = executor2.isAlive();
        // 等待线程2执行完成后在执行
        executor2.join();
        // 判断线程是否活动
        boolean alive1 = executor2.isAlive();

        System.out.println("alive="+alive+",alive1="+alive1+",currentTime="+ System.currentTimeMillis());
        // 睡眠1秒
        DemoExecutor.sleep(1000);
        System.out.println("currentTime="+ System.currentTimeMillis());

    }
}

4.3、interrupt、isInterrupted

  • 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
  • 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true。被设置中断标志的线程将继续正常运行,不受影响。
  • interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就可以这样做。
    1. 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
    2. 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)

调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。

public class DemoExecutor  extends Thread {
    public DemoExecutor(String name) {
        super(name);
    }

    @Override
    public void run() {
        int i = 1;
        while (true){
            System.out.println("" + this.getName() + "-----" + i);

            i++;
            // 判断线程中断标识
            if(isInterrupted()){
                System.out.println("interrupted:"+isInterrupted());
                return;
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {
        DemoExecutor executor2 = new DemoExecutor("李四");
        executor2.start();
        // 判断线程是否活动
        boolean alive = executor2.isAlive();
        // 设置线程中断标识
        executor2.interrupt();
        Thread.sleep(1000);
        boolean alive1 = executor2.isAlive();

        System.out.println("alive="+alive+",alive1="+alive1+",currentTime="+ System.currentTimeMillis());

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值