当new Thread,以及调用start、join等操作时,到

2018-06-15  本文已影响0人  柚子过来

首先看看Thread中定义了哪些常见的变量域:

public class Thread implements Runnable {        //Thread实现自Runable
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
    registerNatives();
}

private volatile String name;                              //线程名
private int            priority;                                  //线程优先级

/* Whether or not the thread is a daemon thread. */
private boolean     daemon = false;                 //是否是守护线程

/* What will be run. */
private Runnable target;                                //线程体Runable

/* The group of this thread */
private ThreadGroup group;                         //线程组

/* The context ClassLoader for this thread */
private ClassLoader contextClassLoader;

/* For autonumbering anonymous threads. */
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;           //ThreadLocalMap 

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;     //可继承的ThreadLocalMap 

/*
 * The requested stack size for this thread, or 0 if the creator did
 * not specify a stack size.  It is up to the VM to do whatever it
 * likes with this number; some VMs will ignore it.
 */
private long stackSize;                 //线程栈大小

/*
 * Thread ID
 */
private long tid;

/* For generating thread ID */
private static long threadSeqNumber;

/*
 * Java thread status for tools, default indicates thread 'not yet started'
 */
private volatile int threadStatus;     //线程状态:NEW-0,RUNNABLE,BLOCKED、WAITING、TERMINATED

当我们new Thread(Runable runable)的时候,调用的构造函数是这样的:

 public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

 public Thread(ThreadGroup group, Runnable target) {
    init(group, target, "Thread-" + nextThreadNum(), 0);
}

 public Thread(ThreadGroup group, Runnable target, String name,
              long stackSize) {
    init(group, target, name, stackSize);
}

可以看到除了传入Runable,我们还可以指定线程所在组、线程栈的大小等,看看init方法里做了什么:

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();         //指定父线程
    SecurityManager security = System.getSecurityManager();
    if (g == null) {                             //设置了线程所在组
        /* Determine if it's an applet or not */
        /* If there is a security manager, ask the security manager
           what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }
        /* If the security doesn't have a strong opinion of the matter
           use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();      //继承父线程的线程组,所以所有启动的线程都是放在同一个线程组main中
        }
    }
    ... ...
    this.group = g;
    this.daemon = parent.isDaemon();      // 守护线程是有继承性的
    this.priority = parent.getPriority();        //优先级也有继承性
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;

    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =                  //继承父线程的inheritableThreadLocals 
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;        //设置线程栈大小

    /* Set thread ID */
    tid = nextThreadID();       //递增生成线程id
}
 private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}

新建了一个线程之后,线程状态是NEW,这时候我们可以调用start启动线程,这时候做了什么:

  public synchronized void start() {

    if (threadStatus != 0)         //只有NEW状态的线程在可以start,不然会抛异常
        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 {
       ... ...
    }
}

private native void start0();

如果我们再调用join方法呢:

 public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;
     ... ...
    if (millis == 0) {
        while (isAlive()) {         //会阻塞其他所有线程直到当前线程执行完毕
            wait(0);
        }
    } else {
        while (isAlive()) {
           ... ...
        }
    }
}

想要中断线程可以使用interrupt,其他的像stop、destroy方法在最新的jdk中将被废弃移除:

   public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();

    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();           // Just to set the interrupt flag
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}

但是注意该方法只是设置了中断标志位,并不是直接中止线程,这时有两种情况:1、线程处于阻塞状态,这时会抛出中断异常。2、线程处于非阻塞状态,那JVM会在"合适的时机"终止线程,我们可以在程序中使用while(isInterrupted)来进行判断进而进行相关处理

还有其他的像getStackTrace()获取线程栈信息、yield()让出CPU重新进入就绪状态等

前面提到线程的各个状态,那哪些操作会影响线程的状态呢:

1、start、join、yield等线程自身的方法
2、Object类的wait/notify/notifyAll方法
3、并发库中的工具,如闭锁、栅栏
4、获取锁操作导致的阻塞
... ...
上一篇 下一篇

猜你喜欢

热点阅读