线程相关
# 线程状态
New
创建线程Runnable
可运行,可能真正执行,也可能在等待CPU时间片。Blocking 阻塞,等待获取排他锁。
Waiting 无限期等待 直到被唤醒。
Timed Wating 限期等待
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 结束");
}
}
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;
}
}
}
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
方法,线程也将一直运行下去。