Java中的多线程
Java中的多线程
一、线程的创建方式
JUC中为Java多线程提供了很多工具,其中创建线程有四种方式:
- 继承
Thread类并重写run()方法:
开发者可以自定义一个类继承Thread类,并重写run()方法。然后创建该类的实例,并调用start()方法执行。
class MyThread extends Thread {
@Override
public void run() {
System.out.println(“Thread running”);
}
}
MyThread t = new MyThread();
t.start(); // 启动线程 - 实现
Runnable接口并传给Thread的构造函数:
一个类不能继承多个类,但是可以实现多个接口。因此可以将目标类实现Runnable接口,并将其传给Thread构造器(也可以直接传lambda表达式给构造器)。
Runnable task = () -> System.out.println(“Runnable running”);
Thread t = new Thread(task);
t.start(); // 启动线程 - 实现
Callable接口,并采用FutureTask类包装,最后传入Thread构造器:
实现Callable接口可以通过FutureTask实例获取线程执行的返回值。
Callabletask = () -> {
System.out.println(“Callable running”);
return 42;
};
FutureTaskfutureTask = new FutureTask<>(task);
Thread t = new Thread(futureTask);
t.start(); // 启动线程 - 通过
Executor框架创建线程池:
通过submit()或者execute()方法执行任务,避免反复创建和销毁线程。
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> System.out.println(“Executor running”));
executor.shutdown();
二、线程的生命周期和状态
Java的线程生命周期中有以下六种状态:
- 新建(NEW) :
当一个线程对象被创建时,Java不会立刻启动这个线程,而是将这个线程置于新建状态下。 - 可运行(RUNNABLE) :
当这个线程对象执行start()方法时,就会进入可运行状态。
tip :可运行状态不代表线程正在运行,线程正在等待时间片分配也是可运行状态。 - 阻塞(BLOCKED) :
当线程试图进入一个同步代码块,但是此时所获取的锁被其它线程占用时,就会进入阻塞状态。 - 等待(WAITING) :
当线程调用Object.wait()、Thread.join()方法时,线程就会进入等待状态,直到被notify()或notifyAll()方法唤醒。 - 超时等待(TIMED_WAITING) :
如果线程调用了带超时时间的方法,如Thread.sleep(long millis)、wait(long timeout)或join(long millis),就会进入超时等待状态。 - 终止(TERMINATED) :
当run()方法执行完毕或未捕获异常而提前结束时,线程就会进入终止状态。
流程如图:
三、线程上下文的切换
线程上下文的切换 ,指的是CPU从一个线程切换到另一个线程是,操作系统需要保存当前线程的执行状态,并加载下一个线程的执行状态,以便它们能够正确运行。执行状态有:寄存器状态 、程序计数器状态 、栈信息 、线程优先级 等。
发生时机
有四种情况:
- CPU时间片耗尽 :
操作系统为每个线程都分配了一定的时间片,当规定时间片内线程没有执行完毕自身任务时,操作系统会强制切换到下一线程,触发线程上下文切换。 - 线程主动让出CPU :
当线程执行到Three.sleep()、Object.wait()等方法时,线程会主动让出CPU,触发线程上下文切换。 - 调用了阻塞类型的系统中断 :
当线程进行IO操作时,因为IO操作通常需要等待外部资源,所以线程会被挂起,触发线程上下文切换。 - 被结束或者终止运行 。
线程上下文切换虽然可以实现多任务并发执行,但是它也会带来CPU时间消耗 、缓存失效 、资源竞争 等问题,增大了性能损耗。
切换过程
- 保存当前线程上下文至内存 :包括寄存器状态、程序计数器状态、栈信息等。
- 根据线程调度算法 ,找到下一个要执行的线程:调度算法包括时间片轮转 、优先级调度 等。
- 加载下一个线程的上下文信息 :从内存中恢复寄存器状态、程序计数器状态和栈信息。
- CPU执行被加载线程的代码 。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Aromatic!



