并发编程-Thread源码解读


之所以解读Thread源码是因为这个是创建启动线程最重要的类之一,而且在多线程编程中会经常用到的一个类,对Thread类的深入了解有助于以后在多线程Code方面得心应手,也是对自己的一个提升。

Thread构造函数

//空构造函数
public Thread();
//通过传入实现了Runnable接口的线程单元构造一个线程
public Thread(Runnable target);
//通过指定线程组和实现了Runnable接口的线程单元构造一个线程
public Thread(ThreadGroup group, Runnable target);
//通过指定线程名构造一个线程
public Thread(String name);
//通过制定一个线程组和线程名构造一个函数
public Thread(ThreadGroup group, String name);
//通过实现Runnable接口的线程单元和指定线程名构造一个线程
public Thread(Runnable target, String name);
//通过指定线程组,实现了Runnable接口的线程单元,以及线程名构造一个线程
public Thread(ThreadGroup group, Runnable target, String name);
//通过指定线程组,实现了Runnable接口的线程单元,线程名以及栈深度来构造一个函数
public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize);

Thread类为我们提供了丰富的构造函数来创建一个线程。对于上面的构造函数作一些特别说明:
线程名:如果不指定线程名,那么系统就按照默认的Thread-i进行生成,i每次作++计算。
线程组:如果不指定线程组,那么系统就会将创建的线程加入到当前线程的父线程所在的线程组里,线程组区别于线程池,后续会进行解释。
线程标识:每个线程都会有一个线程名和其对应的线程组,通过tid进行标识。

重要的初始化方法

所有的构造方法都会调用下面这个线程初始化的init私有方法,这个方法主要作用就是根据给定的参数以及默认参数进行线程初始化工作,后两个参数在构造线程的时候,按照默认值进行设值,具体声明如下:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals)

主要初始化工作有
1):线程名指定
2):线程组指定
3):是否守护线程设置
4):线程优先级设置
5):线程ID设置

几个重要的变量

//线程id,标识线程
private long tid;
//线程名,标识线程
private volatile String name;
//指定线程优先级
private int priority;
//标识该线程是否是守护线程
private boolean daemon = false;
//标识线程的状态,0表示线程未启动(就是NEW状态)
private volatile int threadStatus = 0;
//线程共享变量,由ThreadLocal持有
ThreadLocal.ThreadLocalMap threadLocals = null;

以上的几个标量是编码时常碰到的,而且这几个变量在线程中扮演了相当重要的角色。关于ThreadLocal后续文章会专门进行解释。

几个重要的API

1.start方法

    /**
     *启动线程,run方法由JVM调用,不能被重复调用
     */
    public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    //来自底层的土著方法
    private native void start0();

这个方法没啥好解释的,没啥代码量,一看就懂,算了还是解释一下,加深下印象吧!就是调用了start0()这个JNI本地方法,然后run方法就是通过这个start0方法,由JVM进行调用。
2.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);
    }

这个方法也很简单,给定一个休眠时间,然后线程暂停执行,而且可中断,不过有个重要特性,那就是不会放弃monitor锁的所有权,多说一句,每个对象都会有一个与之关联的monitor,用来进行资源的竞争,一个monitor的lock只能被一个线程在同一时间获得。
3.yield方法

 public static native void yield();

这个方法的作用会将当前线程从Running状态切换到Runnable状态,CPU资源紧张情况下是忽略的,所以是一种启发式的方法,不太常用。
4.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 (isAlive()) {
                wait(0);//Object提供的方法
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
 public final void join() throws InterruptedException;
 public final synchronized void join(long millis, int nanos) throws InterruptedException;

这个方法是通过synchronized结合Object提供的**wait(long timeout)**方法实现。join某个线程A,会是当前线程B进入等待,直到线程A结束生命周期,或者达到给定时间,那么此期间线程B处于Blocked状态,而不是线程A,好好理解一下。
5.interrupt相关方法

    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0(); // 仅设置一个interrupt标识
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
    //判断当前线程是否被中断,该方法会擦除掉线程的interrupt标识
    public static boolean interrupted()//判断线程是否被中断,该方法仅仅是对interrupt标识的一个判断,标识不会发生任何改变。
    public boolean isInterrupted()//本地方法,上面两个方法都是调用此方法进行返回
    private native boolean isInterrupted(boolean ClearInterrupted);

我们的调用的一些方法,如sleep,wait,join等会让当前线程进入阻塞状态,而调用当前线程的interrupt方法,会打断这种阻塞,因此是一种可中断方法。
还有几个可能不是很常用到的方法,如下
6.isAlive方法

//判断线程是否存活
public final native boolean isAlive();

7.setDaemon方法

//是否设置线程为守护线程
public final void setDaemon(boolean on)

8.setPriority方法

//设置线程的优先级,1,5,10值越大优先级越高
public final void setPriority(int newPriority)

几个重要概念

守护线程

1.什么是守护线程
这是一类比较特殊的线程,我们的幕后英雄们,比如JDK的垃圾回收线程。一般我们的用户线程执行完成后,没有任何非守护线程之后,只剩下守护线程的时候,这个时候系统就会退出。
2.守护线程有什么作用,如何设置
一般用于处理后台工作,通过setDaemon(boolean on)方法进行设置.

线程组

1.什么是线程组
每个线程都会隶属于一个组,要么设置为默认的组,要么指定相应的线程组,线程组的出现,是为了组织线程而不是管理线程,线程池是对线程进行管理的最好说明。
2.如何创建线程组

//获取当前线程组
ThreadGroup cgroup = Thread.currentThread().getThreadGroup();
//定义一个线程组
ThreadGroup group1 = new ThreadGroup("group1");
//定义一个线程组,指定group1为父group
ThreadGroup group2 = new ThreadGroup(group1,"group2");

以上是对Thread类的基本情况做了简单的解读。

猜你喜欢

转载自blog.csdn.net/weixin_30484149/article/details/86484142
今日推荐