Java线程

线程的创建和执行

创建线程

  1. 通过继承 Thread 类并重写 run() 方法创建新线程。
class NewThread extends Thread {

    @Override
    public void run() {
        // 线程运行的业务逻辑
    }
}
  1. 通过实例化 Thread 对象创建新线程。

线程对象 Thread 的构造函数需要一个实现了 Runnable 接口的 run 方法的对象,并赋值给 target 属性:

Runnable r = () -> { task code };
Thread t = new Thread(r);

在内部,run() 方法运行调用 target 属性(即 Runnable 实现对象),的 run() 方法。

该方法更具优势:

启动线程

调用 Thread 对象的 start 方法,将创建一个执行 run 方法的新线程。

线程中断

  1. 执行完毕中断

    线程内的逻辑执行完成后或出现了没有捕获的异常时,线程将终止。

  2. interrupt中断

    当对线程调用 interrupt() 方法时,线程的中断状态将被置位。

无论是主动检测中断状态位还是捕获到 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线程的状态

Java 线程有 66 种状态:

  1. NEW(初始化状态)
  2. RUNNABLE(可运行 / 运行状态)
  3. BLOCKED(阻塞状态)
  4. WAITING(无时限等待)
  5. TIMED_WAITING(有时限等待)
  6. TERMINATED(终止状态)

Runnable 切换到 BLOCKED

线程等待 synchronized 的隐式锁时,会触发这种转换。等线程获得 synchronized 锁时,又会从 BLOCKED 转换到 RUNNABLE

线程调用阻塞式API时,尽管操作系统层面会转换到休眠状态,但在JVM层面,依旧是RUNNABLE状态。

Runnable 切换到 WAITING

  1. 获得 synchronized 锁的线程,调用无参数Object.wait() 方法。
  2. 调用无参数Thread.join() 方法。
  3. 调用无参数LockSupport.park() 方法。待调用 LockSupport.unpark(Thread thread) 方法时,可将目标线程唤醒,目标线程状态从 WAITING 切换到 RUNNABLE

Runnable 切换到 TIMED_WAITING

  1. 调用带超时参数Thread.sleep(long millis) 方法。
  2. 获得 synchronized 锁的线程,调用带超时参数Object.wait(long timeout) 方法。
  3. 调用带超时参数Thread.join(long millis) 方法。
  4. 调用带超时参数LockSupport.parkNanos(Object blocker, long deadline) 方法。
  5. 调用带超时参数LockSupport.parkUntil(long deadline) 方法。

从 RUNNABLE 切换到 TIMED_WAITING 和 WAITING 状态的区别,仅仅是触发条件多了超时参数。

NEW 切换到 Runnable

Java 刚创建出 Thread 对象,该线程就是 NEW 状态。此时该线程不会被系统执行,必须调用 start() 方法,会将线程状态切换为 RUNNABLE,系统便会根据调度算法适时运行。

Runnable 切换到 TERMINATED

线程执行完 run() 方法或被强行中断后主动退出,即切换到 TERMINATED 状态。