Java Thread类 API解读

410 阅读4分钟

创建Thread的两种方式

网上看到说啥的都有,最权威的还得看JDK文档,所以各位小伙伴在遇到这个面试题的时候不要答错哦!!! image.png

一种是class继承Thread,重写run方法,在run方法里实现我们自己的逻辑

class MyThread extends Thread{

	@override
	public void run(){
		/*我们自己的逻辑*/
	}
	
}

new MyThread().start();

一种是class实现Runnable接口,重写run方法,然后新建Thread类

class MyTask implements Runnable{

	@override
	public void run(){
		/*我们自己的逻辑*/
	}
}

MyTask task = new MyTask;
new MyThread(task).start();

线程状态

底层都是一个变量表示

private volatile int threadStatus = 0;

NEW

还没启动的线程处于这种状态

RUNNABLE

在Java虚拟机中执行的线程处于这种状态。

BLOCKED

线程被阻塞等待锁释放,进入synchronized代码块/synchronized修饰的方法 或者 重新synchronized代码块/synchronized修饰的方法后,调用Object#wait()进入此状态。

WAITING,无限期地等待另一个线程执行特定操作的线程处于此状态。

  1. Object#wait()不指定超时时间
  2. Thread#join()不指定超时时间
  3. LockSupport#park()

TIMED_WAITING

指定的时间内等待另一个线程执行特定操作的线程处于此状态。

  1. Thread#sleep()
  2. Object#wait()指定超时时间
  3. Thread#join()指定超时时间
  4. LockSupport#parkNanos
  5. LockSupport#parkUntil

TERMINATED

执行完毕退出的线程处于这个状态。

一个线程在一个时间点只能有一个状态,状态仅仅是虚拟机定义的,跟操作系统无关。

上述可知:

  1. 在 Java 调用 start() 后,操作系统中才真正出现了一个线程,并且立刻运行。

  2. Java中的线程,和操作系统内核中的线程,是一对一的关系。

  3. 调用 start 后,线程状态变为 RUNNABLE,这是由 native 方法里的某部分代码造成的。

线程名称

没有指定默认就是「Thread-N」

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

线程组的概念(不是很常用)

每个线程都可以属于一个线程组,如果我们没有指定线程组,默认使用的是当前执行线程的线程组,比如main线程的线程组。

if (g == null) {
    g = parent.getThreadGroup();
}

daemon thread(守护线程)

父线程就是当前执行创建线程的线程。

如果我们不指定一个线程是否是守护线程,就会用父线程的isDaemon状态。默认情况下,父线程是守护线程,子线程也是,反之亦然。

如果一个线程是守护线程,在主线程退出后,守护线程也会自动退出。如果是非守护线程,则主线程退出后,非守护线程不会退出,而是等到所有逻辑执行完后退出

this.daemon = parent.isDaemon();

Thread ID

每个线程还有一个线程ID的概念,就是用来标识的


/* Set thread ID */
tid = nextThreadID();

private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}

start方法

  1. 当我们调用start方法的时候,jvm就会默认调用我们实现的run方法。
  2. 不能多次调用一个线程的start方法,否则抛出IllegalThreadStateException异常

sleep方法

  1. 让当前线程睡眠,睡眠时间我们可以自己指定毫秒数。
  2. sleep线程睡眠之后,并不会丢失拥有的锁。
public static native void sleep(long millis) throws InterruptedException;

JDK1.5之后引入了TimeUnit这个类,我们可以很方便的去指定要睡眠的时间。for example:

TimeUnit.HOURS.sleep(1); //1小时
TimeUnit.MINUTES.sleep(1); //1分钟
TimeUnit.SECONDS.sleep(1);//1秒
TimeUnit.MILLISECONDS.sleep(1);//1毫秒

yield方法

提示当前线程是否愿意放弃当前使用的处理器,一般是用于测试和调试的方法。

join方法

main线程创建子线程,然后调用子线程的join方法,就会阻塞住main线程,直到子线程执行完毕。当然看代码我们也可以指定阻塞多少毫秒,等你多久就不等了!

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");
    }

		//下面其实就是阻塞的关键代码,使用了Object.wait()方法
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
				//millis其实就是等待的时间,最多等待多少秒
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

interrupted方法

case1

有一个标志位,isInterrupted,判断线程是否中断,调用interrupt方法后,就会返回true,可以停止执行一些动作。

interrupt方法是和isInterrupted这个标志位来配合使用的,并不会真的打断线程的运行。

Thread subThread = new Thread(()->{
    try {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("子线程执行....");
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
subThread.start();
Thread.sleep(2000);
System.out.println("中断子线程的执行.....");
//打断子线程的执行后,isInterrupted的标志位就会变成true,
//interrupt
subThread.interrupt();
System.out.println(subThread.isInterrupted());

/*
output
------------------
子线程执行....
子线程执行....
子线程执行....
子线程执行....
中断子线程的执行.....
true

//
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.mazai.concurrent.Demo2.lambda$main$0(Demo2.java:13)
	at java.lang.Thread.run(Thread.java:748)
*/

case2

同时interrupt方法也可以打断线程的休眠,让线程从休眠中醒过来。

public static void main(String[] args) {
    MyThread myThread = new MyThread();
    myThread.start();
    myThread.interrupt();
}

private static class MyThread extends Thread{
    private boolean isRun = true;
    @Override
    public void run(){
        while (isRun){
            try {
                System.out.println("睡5s......");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                isRun = false;
            }
        }
    }
}

/*
output
-----------------------
睡5s......
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.mazai.concurrent.Demo2$MyThread.run(Demo2.java:21)
*/