目录:
一.认识线程和进程
二.Thread 类的基本用法
三.线程的状态
一.认识线程和进程:
1.进程:
进程是一个具有一定独立功能的程序在一个数据集合上依次动态执行的过程。进程是一个正在执行的程序的实例,包括程序计数器、寄存器和程序变量的当前值
可以打开任务管理器查看一下:
![]()
1.2.进程的特征:
进程是操作系统进行资源分配和调度的基本单位。
2.线程:
一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码.例子:⼀家公司要去银行办理业务,既要进行财务转账,⼜要进行福利发放,还得进⾏缴社保。如果只有张三⼀个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三⼜找来两位同事李四、王五⼀起来帮助他,三个⼈分别负责⼀个事情,分别申请⼀个号码进⾏排队,⾃此就有了三个执行流共同完成任务,但本质上他们都是为了办理⼀家公司的业务。此时,我们就把这种情况称为多线程,将⼀个⼤任务分解成不同⼩任务,交给不同执⾏流就分别排队 执⾏。其中李四、王五都是张三叫来的,所以张三⼀般被称为主线程。
3.为啥要有线程:
并发编程已经成为 "刚需:
3.1.单核 CPU 的发展遇到了瓶颈. 要想提⾼算⼒, 就需要多核 CPU. ⽽并发编程能更充分利⽤多核 CPU资源
3.2.有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做⼀些其他的⼯作, 也需要⽤到并发编程
3.3.其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量.
创建线程比创建进程更快.销毁线程比销毁进程更快.调度线程比调度进程更快.
4.并发和并行:并行:并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行,所以无论从微观还是从宏观来看,二者都是一起执行的并发:并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
5.进程和线程的区别:5.1.进程是包含线程的. 每个进程至少有一个线程存在,即主线程5.2.进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间5.3.进程是系统分配资源的最小单位,线程是系统调度的最小单位。5.4.⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带走(整个进程崩溃).6.Java 的线程和操作系统线程的关系:线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了⼀些 API 供用户使用(API :例如 Linux 的 pthread 库).Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进⼀步的抽象和封装.
二.Thread 类的基本用法:
Thread 类是 JVM 用来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关联。用我们上面的例子来看,每个执行流,也需要有⼀个对象来描述,类似下图所示,而Thread 类的对象就是面来描述⼀个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。1.Thread 的几个常见的属性:
1.1.ID 是线程的唯⼀标识,不同线程不会重复1.2.名称是各种调试工具用到1.3.状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明1.3.优先级高的线程理论上来说更容易被调度到1.4.关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有非后台线程结束后,才会结束运行。1.5.是否存活,即简单的理解,为 run 方法是否运行结束了2.线程创建:
调用start 方法, 才真的在操作系统的底层创建出⼀个线程
继承 Thread, 重写 run
class MyThread extends Thread { @Override public void run() { while (true) { System.out.println("这⾥是线程运⾏的代码"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Demo1 { public static void main(String[] args) throws InterruptedException { MyThread t = new MyThread(); t.start(); while (true) { System.out.println("这里是主线程"); Thread.sleep(1000); } } }实现 Runnable, 重写 run:
class MyRunnable implements Runnable { @Override public void run() { while (true) { System.out.println("这⾥是线程运⾏的代码"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Demo2 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new MyRunnable()); t.start(); while (true) { System.out.println("这里是主线程"); Thread.sleep(1000); } } }继承 Thread, 重写 run, 使用lambda 表达式:
public class Demo6 { /** * isInterrupted()方法说明:如果当前线程被中断,则返回 true;否则返回 false * currentThread()方法说明: 返回当前正在执行的线程对象的引用 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { // 这个代码是在 lambda 中 (也就是在 t 线程的入口方法中) 调用的 // 返回结果就是 t System.out.println("t: " + Thread.currentThread().getName()); while (!Thread.currentThread().isInterrupted()) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { //这个线程掀桌了 throw new RuntimeException(e); } } System.out.println("t 结束"); }); t.start(); Thread.sleep(3000); System.out.println("main 线程尝试终止 t 线程"); t.interrupt(); // 这个代码是在 main 方法中调用的, 此时返回结果就是 main System.out.println("main: " + Thread.currentThread().getName()); } }实现 Runnable, 重写 run, 使用匿名内部类:
public class demo4 { public static void main1(String[] args) { Runnable t = new Runnable() { @Override public void run() { while (true) { System.out.println("这⾥是线程运⾏的代码"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; } }
3.线程中断:
方式一:可以自己写一个布尔类型的成员变量,通过改变该变量结束线程。
代码:
public class Demo5 { private static boolean isFinished = false;//通过一标志来结束线程 public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{ while (!isFinished){ System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread 结束"); }); t.start(); Thread.sleep(3000); isFinished = true; } }注意:lambda表达式不能把这个isFinished标志写在局部变量位置,因为变量捕获机制,拷贝了一份变量给lambda表达式,拷贝的值是不允许改变的,所以捕获的值不可改变,就会报错
方式二:调用 interrupt()方法改变标志位,来中断线程
这里再介绍两个方法:
isInterrupted():如果当前线程被中断,则返回 true;否则返回 false
currentThread(): 返回当前正在执行的线程对象的引用
public class Demo6 { /** * isInterrupted()方法说明:如果当前线程被中断,则返回 true;否则返回 false * currentThread()方法说明: 返回当前正在执行的线程对象的引用 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { // 这个代码是在 lambda 中 (也就是在 t 线程的入口方法中) 调用的 // 返回结果就是 t System.out.println("t: " + Thread.currentThread().getName()); while (!Thread.currentThread().isInterrupted()) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { //这个线程掀桌了 throw new RuntimeException(e); } } System.out.println("t 结束"); }); t.start(); Thread.sleep(3000); System.out.println("main 线程尝试终止 t 线程"); t.interrupt(); // 这个代码是在 main 方法中调用的, 此时返回结果就是 main System.out.println("main: " + Thread.currentThread().getName()); } }4.线程等待:
有时,我们需要等待⼀个线程完成它的⼯作后,才能进⾏⾃⼰的下⼀步⼯作。这里让t1先执行,结束后t2再执行。注意:这里t1和t2都在主线程之前执行。public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { System.out.println("t1 结束"); }); Thread t2 = new Thread(() -> { System.out.println("t2 结束"); }); t1.start(); t1.join(); t2.start(); t2.join(); }
如果都先开启两个线程,这样t2也可能在t1之前执行:因为线程在操作系统下是抢占式执行的。
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { System.out.println("t1 结束"); }); Thread t2 = new Thread(() -> { System.out.println("t2 结束"); }); t1.start(); t2.start(); t1.join(); t2.join(); }
5.获取线程实例:
方法 说明 currentThread() 返回当前线程对象的引用 isInterrupted()如果当前线程被中断,则返回 true;否则返回 false/** * isInterrupted()方法说明:如果当前线程被中断,则返回 true;否则返回 false * currentThread()方法说明: 返回当前正在执行的线程对象的引用 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { // 这个代码是在 lambda 中 (也就是在 t 线程的入口方法中) 调用的 // 返回结果就是 t System.out.println("t: " + Thread.currentThread().getName()); while (!Thread.currentThread().isInterrupted()) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { //这个线程掀桌了 throw new RuntimeException(e); } } System.out.println("t 结束"); }); t.start(); Thread.sleep(3000); System.out.println("main 线程尝试终止 t 线程"); t.interrupt(); // 这个代码是在 main 方法中调用的, 此时返回结果就是 main System.out.println("main: " + Thread.currentThread().getName()); }
注意这个代码会抛出异常:因为isInterrupted不但会改变布尔类型标志位来终止线程还会,唤醒Sleep这样的阻塞方法。
三.线程的状态:
1.线程的所有状态:(1).NEW: 安排了工作, 还未开始行动(2).RUNNABLE: 正在工作中和即将开始工作.(线程正在或者即将在CPU上执行)(3).BLOCKED: 由于锁导致的阻塞
(4).TIMED: 死等没有超时时间的等待
(5).TERMINATED:工作完成了(内核中的线程结束了,但是Thread对象还在)(6).TIMED_WAITING: 有时间上制的等待,
2.这里看线程状态可以用: 本项目jdk中的 jconsole查看
![]()
![]()
情况(1),(6)演示:代码: 有参数的join(),和Sleep可能会让线程陷入有时间上制的等待
public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); System.out.println(t.getState());//NEW状态:还未开始工作 t.start(); t.join(1000 * 10); System.out.println(t.getState());//TIMED_WAITING:阻塞状态 }
![]()
情况.(4).死等状态:代码public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); System.out.println(t.getState());//NEW状态:还未开始工作 t.start(); t.join(); System.out.println(t.getState());//WAITING:死等状态 }
情况(2).RUNNABLE:线程正在或者即将在CPU上执行
public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { while (true) { // 在这里啥都不做 } }); System.out.println(t.getState());//NEW状态:还未开始工作 t.start(); Thread.sleep(1000); System.out.println(t.getState());//RUNNABLE状态:就绪状态 }
情况(3).BLOCKED: 由于锁导致的阻塞
public static void main(String[] args) throws InterruptedException { Object locker1 = new Object(); Object locker2 = new Object(); Object locker3 = new Object(); Thread t1 = new Thread(()->{ synchronized (locker1){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (locker2){ System.out.println("t1 尝试去取t2的locker2锁"); } } }); Thread t2 = new Thread(()->{ synchronized (locker2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (locker1){ System.out.println("t2尝试去取t1的locker1锁"); } } }); t1.start(); t2.start(); t1.join(); t2.join(); }

















