一、进程(Process)
- 进程:操作系统中运行的应用程序
- 进程与进程之间是独立的
- 每个进程都运行在其专用且受保护的内存空间中
二、线程(Thread)
- 一个进程要想执行任务就必须要有线程(一个进程至少要有一个线程)
- 一个进程中的所有任务都在线程中执行
三、线程的串行
- 线程中任务的执行是串行的
- 若要在一个线程中执行多个任务,那么只能一个一个地按顺序地执行这些任务
- 同一时间内,一个线程只能执行一个任务
四、多线程
- 一个进程中可以开启多个线程、所有线程并行(同时)执行不同的任务
- 进程 👉 车间;线程 👉 车间工人
- 多线程技术可以提高程序的执行效率
五、多线程原理
- 同一时间,CPU 的一个核心只能处理一个线程(只有一个线程在工作)
- 多线程并发(同时)执行,其实是 CPU 快速地在多个线程之间调度(切换)
- 如果 CPU 调度线程的速度足够快,就造成了多条线程并发执行的假象
- 如果是多核 CPU,才是真正地实现了多个线程同时执行
- 若线程非常多:① CPU 将在多个线程之间来回调度,效率大量 CPU 资源;② 每条线程被调度执行地频次会降低(线程的执行效率会降低)
六、多线程优缺点
- 优点:① 能适当提高线程的执行效率;② 能适当提高资源利用率(CPU、内存利用率)
- 缺点:① 开启线程会占用一定的内存空间。若开启大量线程,会占用大量的内存空间,降低程序性能;② 线程越多,CPU 在调用线程上的开销越大
七、Java 的默认线程
- 每个 Java 程序启动后会默认开启一个线程,称为主线程(main 方法所在线程)
- 每一条线程都是一个
java.lang.Thread
对象,可通过Thread.currentThread
方法获取当前线程的线程(Thread) 对象
八、开启新线程
(1) new Thread()
public class QQTest { public static void main(String[] args) { // ① 创建了线程对象 musicThread Thread musicThread = new Thread(new Runnable() { @Override public void run() { // 这条线程中要执行的任务 System.out.println("\n播放音乐"); System.out.println(Thread.currentThread()); } }); musicThread.setName("Thread-music"); musicThread.setPriority(10); // ② 开启 musicThread 线程 musicThread.start(); } }
(2) 继承 Thread,重写 run 方法
public class QQTest { public static void main(String[] args) { MusicThread musicThread = new MusicThread(); musicThread.setName("Music-Thread"); musicThread.start(); } } class MusicThread extends Thread { @Override public void run() { // 该线程要执行的任务 System.out.println(getName() + "_播放音乐"); } }
(3) run() 和 start()
public class QQTest { public static void main(String[] args) { Thread newThread = new Thread(() -> { System.out.println(Thread.currentThread()); }); // Thread[Thread-0,5,main] newThread.start(); // Thread[main,5,main] newThread.run(); } }
九、多线程的内存布局
① PC 寄存器(Program Counter Register):每个线程都有自己的 PC 寄存器。
PC 寄存器记录着 JVM 正在执行哪一条语句
② Java 虚拟机栈(Java Virtual Machine Stack):每个线程都有自己的 Java 虚拟机栈
③ 堆(Heap):多个线程共享堆空间
④ 方法区(Method Area):多个线程共享方法区
方法区放代码
⑤ 本地方法栈(Native Method Stack):每个线程都有自己的本地方法栈
十、线程的六种状态
- 可通过 Thread 对象的
getState()
获得线程的状态
① NEW(新建):尚未启动
② RUNNABLE(可运行状态):正在 JVM 中运行【或正在等待操作系统的其他资源(如:处理器)】
③ BLOCKED(阻塞状态):正在等待监视器锁(内部锁)
④ WAITING(等待状态):在等待另一个线程
调用以下方法会进入 WAITING 状态:
✏️ 没有超时值的:Object.wait
✏️ 没有超时值的:Thread.join
✏️ LockSupport.park
⑤ TIMED_WAITING(定时等待状态)
调用以下方法会进入 TIMED_WAITING状态:
✏️ Thread.sleep
✏️ 有超时值的:Object.wait
✏️ 有超时值的:Thread.join
✏️ LockSupport.parkNanos
✏️ LockSupport.parkUntil
⑥ TERMINATED(终止状态):已经执行完毕
public class QQTest { public static void main(String[] args) { // 主线程对象 Thread mainThread = Thread.currentThread(); // RUNNABLE System.out.println(mainThread.getState().name()); // 子线程 Thread subThread = new Thread(); // NEW System.out.println(subThread.getState().name()); } }
十一、sleep、interrupt
- 可通过
Thread.sleep
方法暂停(睡眠)当前线程,进入 WAITING 状态 - 在暂停(睡眠)期间,若调用线程对象的 interrupt 方法中断线程,会抛出
java.lang.InterruptedException
public class QQTest { public static void main(String[] args) { Thread subThread = new Thread(() -> { System.out.println("666"); try { Thread.sleep(3333); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("520"); }); subThread.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("111"); subThread.interrupt(); } }