Java并发_线程生命周期和状态转换(2)

线程生命周期

线程生命周期
线程生命周期

线程的状态

Thread 中的 State 枚举定义了线程的六种状态

NEW,完成线程对象的创建 Thread t = new Thread(); 但还未调用其 start 方法

RUNNABLE,调用 start 方法后,在 JVM 层面开始执行了,但在操作系统层面不一定,因为 JVM 判断线程是否执行是根据该线程是否映射到操作系统的线程,但可能还在等待 CPU 切换过来执行

BLOCKED,若当前线程要执行某个对象的同步方法,但该对象的 synchronized 锁正被其他线程占用,则当前线程阻塞,直到其他线程让出 synchronized 锁

WAITING,若线程调用了不带超时的 Object.wait、Thread.join 方法,则会让出当前占有的锁并进入 WAITING 状态,等待另一个线程执行特定的操作来唤醒,被唤醒后会进入 BLOCKED 状态

TIMED_WAITING,若线程调用了带有超时的 Object.wait、Thread.join 以及睡眠方法 sleep,则会让出当前占有的锁并进入限时等待状态,可以由另一个线程执行特定操作来提前唤醒,也可以等到超时时间到自动苏醒,唤醒后同样是先进入 BLOCKED 状态,但 sleep 睡眠方法因为不会让出锁,所以苏醒后会回到 RUNNABLE 状态继续执行

TERMINATED,任务执行完成,线程结束

线程等待和唤醒 wait、notify、notifyAll

wait、notify、notifyAll 方法是 synchronized 同步锁搭配的线程的等待、唤醒方法,只能在同步代码块、或同步方法中调用,因为传入作为锁的对象需要可以是任意对象,所以定义在 Object 类中

当前线程调用 wait 方法后会暂停并让出锁然后进入 WAITING 状态,直到其他线程调用 notify 或 notifyAll 方法将其唤醒,wait 还有一个可以接收指定超时时间的重载方法,让当前线程进入 TIMED_WAITING 状态,若等待超时则自动苏醒,设置的超时时间若为 0 则表示无限等待,即等同于 wait 方法

notify 方法则是随机唤醒一个正在等待的线程,notifyAll 与 notify 类似,区别在于 notify 是随机唤醒一个,而 notifyAll 是唤醒所有等待线程,所以不能通过 notify 和 notifyAll 实现唤醒指定的线程

将正在等待的线程称为等待线程,负责唤醒等待线程的线程称为唤醒线程,则在对等待线程和唤醒线程加锁时,传入的对象需要为同一对象,即对应同一把锁,可理解为当前线程调用锁对象的 wait 方法让出锁并使自己等待,唤醒线程获取到锁,调用锁对象的 notify 方法唤醒等待线程,如果不是同一把锁,则会抛出 IllegalMonitorStateException 异常

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class Test {
  public static void main(String[] args) {
    String s = "lock";
    MyThread t = new MyThread(s);
    //传入Thread-0线程对象作为锁对象
    synchronized(s) {
      try {
        System.out.println(
          Thread.currentThread().getName() + "开启" + t.getName());
        t.start();
        System.out.println(
          Thread.currentThread().getName() + "调用锁对象的wait方法使自己等待");
        //保证锁对象是同一个
        s.wait();
        System.out.println(Thread.currentThread().getName() + "被唤醒");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
class MyThread extends Thread {
  String s;
  MyThread(String s) {
    this.s = s;
  }
  public void run() {
    //传入自己对象作为锁对象
    synchronized (s) {
      System.out.println(
        Thread.currentThread().getName() + "调用锁对象的notify方法唤醒main线程");
      //保证锁对象是同一个
      s.notify();
      System.out.println(
        Thread.currentThread().getName() + "调用notify方法后不会立刻让出锁");
    }
  }
}
//main开启Thread-0
//main调用锁对象的wait方法使自己等待
//Thread-0调用锁对象的notify方法唤醒main线程
//Thread-0调用notify方法后不会立刻让出锁
//main被唤醒

可以直接用 MyThread 对象来当作锁对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Test {
  public static void main(String[] args) {
    MyThread t = new MyThread();
    //传入Thread-0线程对象作为锁对象
    synchronized(t) {
      try {
        System.out.println(
          Thread.currentThread().getName() + "开启" + t.getName());
        t.start();
        System.out.println(
          Thread.currentThread().getName() + "调用锁对象的wait方法使自己等待");
        t.wait();
        System.out.println(Thread.currentThread().getName() + "被唤醒");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
class MyThread extends Thread{
  public void run() {
    //传入自己对象作为锁对象
    synchronized (this) {
      System.out.println(
        Thread.currentThread().getName() + "调用锁对象的notify方法唤醒main线程");
      notify();
      System.out.println(
        Thread.currentThread().getName() + "调用notify方法后不会立刻让出锁");
    }
  }
}
//main开启Thread-0
//main调用锁对象的wait方法使自己等待
//Thread-0调用锁对象的notify方法唤醒main线程
//Thread-0调用notify方法后不会立刻让出锁
//main被唤醒

线程挂起和继续 suspend、resume

被 suspend 方法挂起的线程需要等到 resume 方法执行后才能继续执行,在 Thread 类中定义,已被废弃,因为被 suspend 挂起的线程不会让出锁,如果其对应的 resume 方法在之前就执行了,挂起的线程将永远阻塞并占有锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {
  public static void main(String[] args) {
    Thread t = new MyThread();
    t.start();
    //如果不让主线程等待,则很有可能
    //线程t还没有执行suspend,而主线程已经执行了resume
    //导致线程t永远挂起,占用锁
    //Thread.sleep(1000);
    t.resume();
    System.out.println(t.getName() + " " + t.getState());
  }
}
class MyThread extends Thread {
  public void run() {
    synchronized (MyThread.class) {
      System.out.println(Thread.currentThread().getName());
      Thread.currentThread().suspend();
    }
  }
}
//Thread-0 RUNNABLE
//Thread-0

线程睡眠 sleep

sleep 是 Thread 类的静态本地方法,可以使当前线程暂停并进入 TIMED_WAITING 状态,但不会让出锁,有两个重载的方法

sleep(long millis) 让当前正在执行的线程在指定的毫秒数内睡眠 sleep(long millis, int nanos) 让当前正在执行的线程在指定的毫秒数加上纳秒数内睡眠

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Test{
  public static void main(String[] args){
    MyThread t = new MyThread();
    t.start();
  }
}
class MyThread extends Thread {
  public void run() {
    try {
      for (int i = 0; i < 3; i++) {
        System.out.println(Thread.currentThread().getName() + i);
        if (i == 1)
          //休眠2s
          Thread.sleep(2000);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

线程让步 yield

yield 是 Thread 类的静态本地方法,可以使当前线程暂停并让出 CPU,让处于 RUNNABLE 状态的线程重新竞争 CPU 的执行,有可能当前线程调用 yield 方法让出了 CPU,但马上又竞争到 CPU 继续执行,并且 yield 方法不会让出锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Test{
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    t1.start();
    t2.start();
  }
}
class MyThread extends Thread {
  public void run() {
    for (int i = 0; i < 5; i++) {
      System.out.println(Thread.currentThread().getName() + " i = " + i);
      if (i == 3)
        Thread.yield();
    }
  }
}
//Thread-0 i = 0
//Thread-0 i = 1
//Thread-0 i = 2
//Thread-0 i = 3  //i==3时让出了CPU
//Thread-1 i = 0  //Thread-1开始执行
//Thread-1 i = 1
//Thread-1 i = 2
//Thread-1 i = 3
//Thread-0 i = 4
//Thread-1 i = 4
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test{
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    t1.start();
    t2.start();
  }
}
class MyThread extends Thread {
  public void run() {
    synchronized (MyThread.class) {
      for (int i = 0; i < 3; i++) {
        System.out.println(Thread.currentThread().getName() + " i = " + i);
        if (i == 1)
          //虽然让步了,但不会让出锁
          Thread.yield();
      }
    }
  }
}
//Thread-0 i = 0
//Thread-0 i = 1
//Thread-0 i = 2
//Thread-1 i = 0
//Thread-1 i = 1
//Thread-1 i = 2

线程加入 join

join 方法在 Thread 类中定义,可以让一个线程等待另一个线程执行结束后再继续运行,有可以指定等待超时时间的重载方法 join(long millis)、join(long millis , int nanos),让线程等待的实现依赖于 wait 方法,所以当前线程调用 join 方法后会进入 WAITING 或 TIMED_WAITING 状态,并且让出锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test{
  public static void main(String[] args) {
    try {
      MyThread t = new MyThread();
      t.start();
      System.out.println(Thread.currentThread().getName() + "开始执行");
      System.out.println("中途加入" + t.getName());
      //将线程t加入join到主线程main中,main线程进入WAITING状态
      t.join();
      System.out.println(Thread.currentThread().getName() + "继续执行");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
class MyThread extends Thread {
  public void run() {
    System.out.println(Thread.currentThread().getName() + "执行");
  }
}
//main开始执行
//中途加入Thread-0
//Thread-0执行
//main继续执行

join 方法的源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final void join() throws InterruptedException {
  join(0);
}
//synchronized方法,锁对象为this
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()) {
      //获取到的锁对象为this当前对象
      wait(0);
    }
  } else {
    while (isAlive()) {
      long delay = millis - now;
      if (delay <= 0) {
        break;
      }
      //有超时时间的等待
      wait(delay);
      now = System.currentTimeMillis() - base;
    }
  }
}

被 synchronized 修饰的 join 方法锁对象为 this,所以内部调用的是 this 对象的 wait 方法,即外部是哪个对象调用的 join 方法,实际上则是调用了那个对象的 wait 方法,即将线程 t 加入到主线程 t.join(),实际上是调用了被 join 的线程对象 t 的 wait 方法让主线等待

但是在 join 方法源码中并没有 notify 方法将主线程唤醒,查看 thread.cpp 源码

1
2
3
4
5
6
7
8
9
//src\share\vm\runtime\thread.cpp
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
  ensure_join(this);
  //省略...
}
static void ensure_join(JavaThread* thread) {
  lock.notify_all(thread);
	//省略...
}

结果是由 JVM 调用的,在线程 t 执行完关闭之前,调用 this 的 notifyAll 方法来唤醒的主线程

线程终止 stop

Thread 提供了 stop 方法让线程立即终止,但由于是强行停止,可能导致数据不一致,现已经废弃

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Test{
  public static void main(String[] args) throws InterruptedException {
    MyThread t = new MyThread();
    t.start();
    //主线程等待1ms
    Thread.sleep(1);
    //然后将t1线程强制终止
    t.stop();
  }
}
class MyThread extends Thread {
  public void run() {
    while (true) {
      for (int i = 0; i < 5; i++) {
        System.out.println(Thread.currentThread().getName() + " i = " + i);
      }
    }
  }
}
//...
//Thread-0 i = 0
//Thread-0 i = 1
//Thread-0 i = 2
//Thread-0 i = 3
//Thread-0 i = 4
//Thread-0 i = 0
//Thread-0 i = 1   //执行到一半被强行终止了

可以定义一个 flag 来决定线程终止时机

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Test{
  public static void main(String[] args) throws InterruptedException {
    MyThread t = new MyThread();
    t.start();
    //主线程等待1ms
    Thread.sleep(1);
    //然后调用自定义的停止方法
    t.myStop();
  }
}
class MyThread extends Thread {
  private boolean flag = true;
  public void myStop() {
    flag = false;
  }
  public void run() {
    while (flag) {
      for (int i = 0; i < 5; i++) {
        System.out.println(Thread.currentThread().getName() + " i = " + i);
      }
    }
  }
}
//Thread-0 i = 0
//Thread-0 i = 1
//Thread-0 i = 2
//Thread-0 i = 3
//Thread-0 i = 4

线程中断 interrupt

Thread 提供了 interrupt 中断方法,不会使线程立即退出,而是给线程发送一个通知,收到通知后如何处理由线程自己决定,与线程中断有关的方法有以下三个

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//中断线程
public void interrupt() {
  if (this != Thread.currentThread())
    checkAccess();
  //private final Object blockerLock = new Object();
  synchronized (blockerLock) {
    Interruptible b = blocker;
    if (b != null) {
      interrupt0();           // Just to set the interrupt flag
      b.interrupt(this);
      return;
    }
  }
  interrupt0();
}
//只会将线程的中断标记置为true,并不会直接中断线程
private native void interrupt0();
1
2
3
4
5
6
7
8
9
//判断是否中断,并清除当前中断状态,即将中断标志置为false
public static boolean interrupted() {
  return currentThread().isInterrupted(true);
}
//判断中断标记是否为true,不清除当前中断状态
public boolean isInterrupted() {
  return isInterrupted(false);
}
private native boolean isInterrupted(boolean ClearInterrupted);

interrupted 方法测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Test {
  public static void main(String[] args) {
    System.out.println(Thread.interrupted()); //false
    //中断当前线程
    Thread.currentThread().interrupt();
    System.out.println(Thread.interrupted()); //true
    //当前线程的中断标志被interrupted静态方法重置了
    System.out.println(Thread.interrupted()); //false
  }
}

isInterrupted 方法测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test {
  public static void main(String[] args) {
    MyThread t = new MyThread();
    t.start();
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    t.interrupt();
  }
}
class MyThread extends Thread {
  @Override
  public void run() {
    while (true) {
      System.out.println(this.isInterrupted());
    }
  }
}
//...
//false
//false    //主线程睡眠的1s内t1线程中断标志为fasle
//true     //t1被中断后中断标志变为true
//true     //isInterrupted方法不会重置中断标志
//...

中断处于运行状态的线程

1
2
3
4
5
6
7
@Override
public void run() {
  //isInterrupted方法判断线程的中断标志是否被置为true
  while (!isInterrupted()) {
    //...
  }
}

中断正处于等待状态的线程,中断标记会被清除,同时产生一个 InterruptedException 异常

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class Test {
  public static void main(String[] args) {
    MyThread t = new MyThread();
    t.start();
    t.interrupt();
  }
}
class MyThread extends Thread {
  @Override
  public void run() {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
      System.out.println(this.isInterrupted()); //false
      System.out.println(this.isInterrupted()); //false
    }
  }
}

线程优先级 priority

每个线程都有优先级,范围从 1 到 10,默认 5,优先级高的先执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Test {
  public static void main(String[] args) {
    Thread t1 = new MyThread();
    Thread t2 = new MyThread();
    t1.setPriority(1);
    t2.setPriority(10);
    t1.start();
    t2.start();
  }
}
class MyThread extends Thread {
  public void run() {
    for (int i = 0; i < 3; i++) {
      System.out.println(Thread.currentThread().getName() + " " + i);
    }
  }
}
//Thread-1 0
//Thread-1 1
//Thread-1 2
//Thread-0 0
//Thread-0 1
//Thread-0 2

守护线程 daemon

用户线程是需要执行的任务,守护线程是在后台默默执行的服务,若用户线程都执行完了只剩守护线程,则 JVM 会自动关闭

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Test {
  public static void main(String[] args) {
    Thread t1 = new MyDaemon();
    Thread t2 = new MyThread();
    //设置t1为守护线程
    t1.setDaemon(true);
    t1.start();
    t2.start();
  }
}
class MyDaemon extends Thread {
  public void run() {
    while (true) {
      System.out.println(Thread.currentThread().getName() + "守护线程");
    }
  }
}
class MyThread extends Thread {
  public void run() {
    for (int i = 0; i < 3; i++) {
      System.out.println(
        Thread.currentThread().getName() + "用户线程执行 " + i);
    }
    System.out.println(
      Thread.currentThread().getName() + "用户线程执行完后结束");
  }
}
//Thread-0守护线程
//Thread-1用户线程执行 0
//Thread-0守护线程
//Thread-1用户线程执行 1
//Thread-0守护线程
//Thread-1用户线程执行 2
//Thread-0守护线程
//Thread-0守护线程
//Thread-1用户线程执行完后结束
//Thread-0守护线程
//Thread-0守护线程
//Thread-0守护线程

在用户线程 main 和 t2 执行完后,只剩守护线程 t1,所以 JVM 自动退出


以上内容是玉山整理的笔记,如有错误还请指出