线程的创建和执行
创建线程
- 通过继承
Thread
类并重写run()
方法创建新线程。
class NewThread extends Thread {
@Override
public void run() {
// 线程运行的业务逻辑
}
}
- 通过实例化
Thread
对象创建新线程。
线程对象 Thread
的构造函数需要一个实现了 Runnable
接口的 run
方法的对象,并赋值给 target
属性:
Runnable r = () -> { task code };
Thread t = new Thread(r);
在内部,run()
方法运行调用 target
属性(即 Runnable
实现对象),的 run()
方法。
该方法更具优势:
- 适合多个线程共享同一个处理任务对象,执行相同的任务。
- 增加程序的健壮性,实现解耦操作,代码可被多个线程共享,代码和线程独立。
- 线程池只能放入传入了
Runnable
实现类的线程。
启动线程
调用 Thread
对象的 start
方法,将创建一个执行 run
方法的新线程。
线程中断
-
执行完毕中断
线程内的逻辑执行完成后或出现了没有捕获的异常时,线程将终止。
-
interrupt中断
当对线程调用
interrupt()
方法时,线程的中断状态将被置位。
- RUNNABLE状态的线程可通过
isInterrupted()
方法检测这个状态位,以判断是否被中断。 - 被阻塞的线程(调用
sleep()
或wait()
方法)无法主动检测中断状态位,但是被中断时,会触发InterruptedException
异常,通过捕获,可对中断进行处理。
无论是主动检测中断状态位还是捕获到
InterruptedException
中断,线程只是受到了中断请求,至于如何处理,将由线程自主决定。
异常处理
run()
方法不能抛出任何受查异常,且非受查异常会导致线程终止。可以为线程指定一个的处理器(实现 Thread.UncaughtExceptionHandler
接口),专门处理未捕获异常。
public class App {
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(new Runnable() {
public void run() {
int j = 10 / 0;
}
});
// 也可以调用 Thread 类的静态方法
// setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
// 为所有线程指定一个默认的处理器。
thread1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程1出现了异常,"+e.getMessage());
}
});
thread1.start();
Thread.sleep(2000);
System.out.println("等待2秒,在看线程1的状态" + thread1.getState().toString());
}
}
输出结果为:
线程1出现了异常,/ by zero
等待2秒,在看线程1的状态TERMINATED
线程通信
suspend 和 resume 已经被弃用,suspend不会释放锁,会导致饥饿。并且在suspend之前运行resume时,会导致线程永久挂起。
wait和notify必须写在同步代码块中,否则会抛出 IllegalMonitorStateException 异常。
wait方法,将当前线程加入等待集合,并且放弃当前持有的对象锁。
park获得许可(permit),unpark提供许可。如果在unpark后调用park,线程将直接消费该许可,无需挂起等待。park方法不会自动释放同步锁。
threadlocal
线程池
线程切换
Java 线程有 种状态:
- NEW(初始化状态)
- RUNNABLE(可运行 / 运行状态)
- BLOCKED(阻塞状态)
- WAITING(无时限等待)
- TIMED_WAITING(有时限等待)
- TERMINATED(终止状态)
Runnable
切换到 BLOCKED
线程等待 synchronized
的隐式锁时,会触发这种转换。等线程获得 synchronized
锁时,又会从 BLOCKED 转换到 RUNNABLE。
线程调用阻塞式API时,尽管操作系统层面会转换到休眠状态,但在JVM层面,依旧是RUNNABLE状态。
Runnable
切换到 WAITING
- 获得
synchronized
锁的线程,调用无参数的Object.wait()
方法。 - 调用无参数的
Thread.join()
方法。 - 调用无参数的
LockSupport.park()
方法。待调用LockSupport.unpark(Thread thread)
方法时,可将目标线程唤醒,目标线程状态从 WAITING 切换到 RUNNABLE。
Runnable
切换到 TIMED_WAITING
- 调用带超时参数的
Thread.sleep(long millis)
方法。 - 获得
synchronized
锁的线程,调用带超时参数的Object.wait(long timeout)
方法。 - 调用带超时参数的
Thread.join(long millis)
方法。 - 调用带超时参数的
LockSupport.parkNanos(Object blocker, long deadline)
方法。 - 调用带超时参数的
LockSupport.parkUntil(long deadline)
方法。
从 RUNNABLE 切换到 TIMED_WAITING 和 WAITING 状态的区别,仅仅是触发条件多了超时参数。
NEW
切换到 Runnable
Java 刚创建出 Thread
对象,该线程就是 NEW 状态。此时该线程不会被系统执行,必须调用 start()
方法,会将线程状态切换为 RUNNABLE,系统便会根据调度算法适时运行。
Runnable
切换到 TERMINATED
线程执行完 run()
方法或被强行中断后主动退出,即切换到 TERMINATED 状态。