java源码赏析--java.lang.Thread

1. 线程状态

 public enum State {
        
    NEW, RUNNABLE, BLOCKED, WAITING,  TIMED_WAITING, TERMINATED;
}

说明:

状态 描述
NEW 至今尚未启动的线程的状态
RUNNABLE 可运行线程的线程状态
BLOCKED 受阻塞并且正在等待监视器锁的某一线程的线程状态
WAITING 某一等待线程的线程状态
TIMED_WAITING 具有指定等待时间的某一等待线程的线程状态
TERMINATED 已终止线程的线程状态。线程已经结束执行

如图:

2. 主要属性


/*
 * 线程名字,通过构造参数来指定  
 */
private volatile String name;

/*
 * 表示线程的优先级,优先级越高,越优先被执行(最大值为10,最小值为1,默认值为5)
 */
private int  priority;

/*
 * 线程是否是守护线程:当所有非守护进程结束或死亡后,程序将停止
 */   
private boolean  daemon = false;

/*
 * 将要执行的任务
 */  
private Runnable target;

/*
 * 线程组表示一个线程的集合。此外,线程组也可以包含其他线程组。线程组构成一棵树,在树中,除了初始线程组外,每个线程组都有一个父线程组。
 */ 
private ThreadGroup group;

/*
 * Thread ID
 */ 
private long tid;

/*
 * 用来生成Thread ID使用
 */ 
private static long threadSeqNumber;

/*
 * 第几个线程,在init初始化线程的时候用来赋给thread.name
 */ 
private static int threadInitNumber


/*
 * 线程从创建到最终的消亡,要经历若干个状态。 
 * 一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)
 */
private volatile int threadStatus = 0;

3. 构造函数

源码

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

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

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

public Thread(String name) {
    init(null, null, name, 0);
}

public Thread(ThreadGroup group, String name) {
    init(group, null, name, 0);
}

public Thread(Runnable target, String name) {
    init(null, target, name, 0);
}

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

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

构造函数中第三个参数为线程名称,而"Thread-" + nextThreadNum()是系统默认的线程名,会将属性中的threadInitNumber加一。

private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}

4. 启动线程

4.1 start()方法

源码

public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0){// 如果状态不是`NEW`
        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 */
        }
    }
}

4.2 run方法

源码

public void run() {
    if (target != null) {
        target.run();
    }
}

5. 守护线程与非守护线程(用户线程)

守护线程与普通线程的唯一区别是:

  • 任何一个守护线程都是整个JVM中所有非守护线程的保姆

  • 守护线程与main同生共死,当main退出,它将终止,而普通线程是在任务执行结束才停止。

  • Java虚拟机在它所有非守护线程已经离开后自动离开。守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。

示例

public class ThreadDemo extends Thread {

    public void run() {
        while (true) {
            for (int i = 1; i <= 100; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread daemonThread = new Thread(new ThreadDemo());
        daemonThread.setName("测试thread");
        // 设置为守护进程
        daemonThread.setDaemon(true);
        daemonThread.start();

        System.out.println("isDaemon = " + daemonThread.isDaemon());
        Thread t = new Thread(new ThreadDemo());// 为非守护线程
        t.start();
    }
}

因为有线程t 的存在,守护线程daemonThread 一直执行。

public static void main(String[] args) {
    Thread daemonThread = new Thread(new ThreadDemo());
    daemonThread.setName("测试thread");
    // 设置为守护进程
    daemonThread.setDaemon(true);
    daemonThread.start();
}

如果只有守护线程daemonThread,程序因为main线程的退出而退出。

注意点:

  • thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

  • 在Daemon线程中产生的新线程也是Daemon的

  • 不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑

6. 主要方法

6.1 sleep(long millis)

源码

public static native void sleep(long millis) throws InterruptedException;

注意点:

  • sleep是指线程被调用时,占着CPU不工作,形象地说明为“占着CPU睡觉”,此时,系统的CPU部分资源被占用,其他线程无法进入。

  • sleep方法不会释放锁。

示例:

public class ThreadDemo extends Thread {

    private static Long i = 0L;

    public void run() {
        while (true) {
            synchronized (ThreadDemo.class) {
                i++;
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new ThreadDemo();
        t.start();

        Thread t1 = new ThreadDemo();
        t1.start();
    }
}

两个线程(A、B)同时执行,当线程A获得同步锁并进入同步块时,线程B会处于BLOCKED状态等待锁。而当线程A执行sleep后,线程A状态为TIMED_WAITING,而由于sleep不会释放锁,所以线程B的状态不会改变。

6.2 yield()

源码

public static native void yield();

注意点:

  • 调用yield方法会让当前线程交出CPU权限,让拥有相同优先级的线程有获取CPU执行时间的机会

  • 调用yield方法并不会让线程进入BLOCKED状态,而是让线程重回RUNNABLE状态, 但不能保证迅速转换。

  • 不会释放锁

6.3 isAlive()

表示线程当前是否为可用状态,如果线程已经启动,并且没有死亡,则返回true,否则为false

源码

public final native boolean isAlive();

6.4 join()

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到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);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

public final void join() throws InterruptedException {
    join(0);
}

注意点:

  • join方法是通过wait方法实现的,所以会释放对象锁。

  • 调用方线程状态为WAITING

  • join需要在start之后调用

示例一

public class ThreadDemo extends Thread {

    private static Long i = 0L;

    public void run() {
        synchronized (this) {
            while (true) {
                i++;
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new ThreadDemo();
        t.start();
        Thread.sleep(1000);
        t.join();
    }
}

此处需要注意的是,此处t.join()不会被执行,main线程会一直等待对象锁而BLOCKED

6.5 interrupt()

Thread.sleepThread.joinObject.wait等在检查到线程的中断状态时,会抛出InterruptedException,同时会清除线程的中断状态.

在java的线程Thread类中有三个方法,比较容易混淆,在这里解释一下:

  • interrupt:设置线程的中断状态

  • isInterrupt:线程是否中断,不会清楚中断状态。

  • interrupted:返回线程的上次的中断状态,并清除中断状态。

示例

public class ThreadDemo extends Thread {

    public void run() {
        System.out.println("即将睡眠");
        try {
            Thread.sleep(1000 * 4);
        } catch (InterruptedException e) {
            System.out.println("被唤醒");
        }
        System.out.println("处理其他事情");
    }

    public static void main(String[] args) throws Exception {
        Thread t = new ThreadDemo();
        t.start();
        Thread.sleep(1000);
        t.interrupt();
    }
}

注意点:

  • Thread.interrupt()方法不会中断一个正在运行的线程

  • 如果线程在调用 Object 类的 wait()wait(long)wait(long, int) 方法,或者该类的 join()join(long)join(long, int)sleep(long)sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个InterruptedException异常。

  • synchronized在获锁的过程中是不能被中断的,意思是说如果产生了死锁,则不可能被中断

你可能感兴趣的:(java源码赏析--java.lang.Thread)