文章目录
文章放置于:https://github.com/zgkaii/CS-Notes-Kz,欢迎批评指正!
1 线程状态简述
Java线程在运行的生命周期中可能处于如下6种不同的状态,在给定的一个时刻,线程只能处于其中的一个状态。
线程状态 | 说明 |
---|---|
NEW | 初始状态,线程刚被创建,但是并未启动(还未调用start方法)。 |
RUNNABLE | 运行状态,JAVA线程将操作系统中的就绪(READY)和运行(RUNNING)两种状态笼统地称为“运行中”。 |
BLOCKED | 阻塞状态,表示线程阻塞于锁。 |
WAITING | 等待状态,表示该线程无限期等待另一个线程执行一个特别的动作。 |
TIMED_WAITING | 超时等待状态,不同于WAITING的是,它可以在指定时间自动返回。 |
TERMINATED | 终止状态,表示当前状态已经执行完毕。 |
线程在自身的生命周期中,并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换。
![](https://img-blog.csdnimg.cn/20200929220833538.png)
2 Thread类常用方法
2.1 start()与run()
void start()启动一个新线程,在新的线程运行run方法中的代码。
@Slf4j
public class ThreadTest {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
log.debug("running...");
}
};
thread.setName("new thread");
thread.start();
log.debug("main thread");
}
}
输出结果如下,可见run()
方法里面内容的调用是异步的。
14:06:43.754 [main] DEBUG com.kai.demo.basic.ThreadTest - main thread
14:06:43.754 [new thread] DEBUG com.kai.demo.basic.ThreadTest - running...
将上面代码的thread.start()
改为 thread.run()
,输出结果如下,可见 run()
方法里面内容的调用是同步的。
14:08:17.974 [main] DEBUG com.kai.demo.basic.ThreadTest - running...
14:08:17.979 [main] DEBUG com.kai.demo.basic.ThreadTest - main thread
2.2 sleep()与yield()
2.2.1 sleep()
调用 sleep()会让当前线程从 RUNNING进入TIMED_WAITING状态(超时等待)。sleep()方法导致了程序暂停执行指定的时间,让出CPU给其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放锁。
Tips:sleep()中指定的时间是线程暂停运行的最短时间,不能保证该线程睡眠到时后就开始立刻执行。
public class TimeWaitingTest extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if ((i) % 5 == 0) {
System.out.println("开启线程数为:" + i);
}
System.out.print(i);
try {
Thread.sleep(1000);
System.out.print(" 线程睡眠1秒!\n");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new TimeWaitingTest().start();
}
}
运行结果:
开启线程数为:0
0 线程睡眠1秒!
1 线程睡眠1秒!
2 线程睡眠1秒!
3 线程睡眠1秒!
4 线程睡眠1秒!
开启线程数为:5
5 线程睡眠1秒!
6 线程睡眠1秒!
7 线程睡眠1秒!
8 线程睡眠1秒!
9 线程睡眠1秒!
可见,线程睡眠到时间就会自动苏醒,并返回到Runnable(可运行)中的就绪状态。
Tips:建议用 TimeUnit 的
sleep()
代替 Thread 的sleep()
来获得更好的可读性。
2.2.2 yield()
调用yield()静态原生方法,也能暂停当前线程,其与sleep()
有何异同呢?
- sleep(long)方法会使线程转入超时等待(TIMED_WAITING)状态,时间到了之后才会转入就绪状态。而yield()方法不能指定时间,不会将线程转入TIMED_WAITING状态,而是强制线程进入READY状态,从而让其它具有相同优先级的等待线程获取执行权。
- 使用sleep(long)方法需要处理异常
InterruptedException
,而yield()不用处理异常。
sleep()与yield()的共同点就是:
- 两者都不释放锁、释放CPU资源。
2.3 wait()
线程中调用Object.wait()
方法时,当前线程就会进入阻塞状态,会释放CPU资源和释放同步锁(类锁和对象锁)。直到其他线程调用此对象的notify()
方法或 notifyAll()
方法。
wait是Object类中的方法,方法内部很简单,使用native方法实现:
// 进入WAITING状态
public final void wait() throws InterruptedException {
wait(0);
}
// 进入TIMED_WAITING状态
public final native void wait(long timeout) throws InterruptedException;
// 进入TIMED_WAITING状态
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
wait()方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException
异常。
如果当前线程在等待之前或在等待时被任何线程中断,则会抛出 InterruptedException
异常。
使用案例:
public class WaitingTest {
public static Object obj = new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
try {
System.out.println(Thread.currentThread().getName() + "=== 调用wait方法,进入WAITING状态,释放锁对象");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "=== 从WAITING状态醒来,获取到锁对象,继续执行");
}
}
}
}, "线程A").start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//每隔3秒 唤醒一次
try {
System.out.println(Thread.currentThread().getName() + "‐‐‐ 等待3秒钟");
Thread.sleep(