线程相关

# 线程状态

  1. New
    创建线程

  2. Runnable
    可运行,可能真正执行,也可能在等待CPU时间片。

  3. Blocking 阻塞,等待获取排他锁。

  4. Waiting 无限期等待 直到被唤醒。

  5. Timed Wating 限期等待

  6. Terminated 终止

# 线程等待与唤醒

# Object类的wait()、notify()、notifyAll()方法

  • wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。

  • 这三个方法的调用都需要在synchronized代码块中进行,也就是必须先获取该对象上的锁,否则会报错java.long.IllegalMonitorStatException

  • wait()方法会先释放锁,然后当前线程进入阻塞等待状态。

  • 如果调用notify或notifyAll先发生,wait方法的调用后发生,那么进入的等待的线程不会被唤醒。

  • 被唤醒的线程不会立刻进入运行状态,而是尝试获取对象上的锁。

# 线程让步 yield()

yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行!

yield()不会释放锁,也不依赖synchronized

# join()

join()定义在Thread类中。调用子线程的join方法可以让当前线程待子线程结束之后再运行。

# 演示用例

import lombok.SneakyThrows;

public class ThreadJoinDemo extends Thread {
 private Thread other ;

 public ThreadJoinDemo(Thread other) {
  this.other = other;
 }

 @SneakyThrows
 @Override
 public void run() {
  System.out.println("等待线程"+other.getName()+"先执行");
  other.join();
  System.out.println("线程"+this.getName()+"执行");
  Thread.sleep(3000);
  System.out.println("线程"+this.getName()+"结束");

 }

 public static void main(String[] args) throws InterruptedException {
  ThreadJoinDemo a = new ThreadJoinDemo(Thread.currentThread());
  a.setName("a");
  ThreadJoinDemo b = new ThreadJoinDemo(a);
  b.setName("b");
  a.start();
  b.start();
  Thread.sleep(6000);
  System.out.println("main 结束");
 }
}
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

# 源码分析

public final void join() throws InterruptedException {
    join(0);
}
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;
          }
      }
  }
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

join方法被synchronized修饰,父线程获得子线程对象的锁才能运行,isAlive方法检查子线程是否存活,如果存活执行wait操作,即父线程继续阻塞等待。如果子线程结束,那么跳出循环,父线程可以继续执行了。

# 线程终止方式

  • 终止处于阻塞状态的线程 线程通过sleep、wait、join等方式进入阻塞状态,若此时将调用线程的interrupt方法将线程中断标记设置为true。由于处于阻塞状态,中断标记会被清除,同时产生InterruptedException异常。

  • 通过中断标记终止线程 设置一个标记,当外部需要中断线程时,通过将该标记设置为false,终止线程。这需要一下条件

    • 线程循环以标记作为条件。
    • 标记需要保证可见性,通常使用volatile修饰。

如果线程不会进入阻塞状态,也没有判断标记,那么即使调用了interrupt方法,线程也将一直运行下去。

上次更新: 2023/04/09, 16:34:32
最近更新
01
go-admin-ui项目仿写练手1-登录页
06-29
02
maven依赖问题
06-17
03
JVM相关命令
02-21
更多文章>