认识线程
概念
线程是什么
1)线程就是一个执行流,每个线程按顺序执行自身代码;
多个线程可以 "同时" 执行多份代码
2)银行例子:
公司办业务,张三(主线程)叫李四、王五一起处理转账、发福利、缴社保,3 个执行流共同完成同一个公司的任务,这就是多线程。张三是主线程 (Main Thread)为什么要有线程
1)并发编程是刚需
a)单核 CPU 遇到性能瓶颈,多核 CPU 需要并发程序才能充分利用算力
b) 任务有 IO 等待场景:
线程等待 IO 的时候,CPU 可以去执行别的任务,不浪费时间
2)线程比进程更轻量
a)创建线程更快
b) 销毁线程更快
c) 操作系统调度线程更快
**3)**进阶优化:线程池、协程进程和线程的区别
银行举例:多进程 = 多个客户各自办业务,票据互相隔离;
多线程 = 同一个公司多个员工办业务,票据共享4 种模型:
1)单线程进程:1 个进程,里面只有 1 条执行流
2)单进程多线程:1 个进程,内部多条执行流
3) 多个单线程进程:多个独立进程,每个进程只有 1 个线程
4) 多个多线程进程:多个独立进程,每个进程内部都有多条线程
Java 线程 和 OS 操作系统线程的关系
1) 线程本质是操作系统内核 提供的机制,
OS 提供 API(如 Linux pthread 库)给程序使用
2) Java 里的Thread类,是对操作系统原生线程 API 的抽象和封装 。
Java 线程底层就是操作系统线程面试题
1)线程是什么?
线程是一条执行流,多个线程可以并发执行代码,
同一个进程内线程共享内存资源
2)进程和线程区别?
①进程是资源分配最小单位,线程是 CPU 调度最小单位;
②进程内存相互隔离,同一进程线程共享内存;
③进程包含线程,一个进程至少一个主线程;
④进程崩溃互不影响;线程崩溃可能造成整个进程退出;
⑤线程创建销毁调度开销远小于进程,更轻量。
3)为什么用多线程?
充分利用多核 CPU;IO 等待时让 CPU 执行其他任务;
线程相比进程轻量,开销更小。
4)Java Thread 和操作系统线程? Java 的 Thread 封装了操作系统原生线程,Java 线程本质就是操作系统线程。第一个多线程程序
public class ThreadDemo { // 自定义线程类,继承Thread private static class MyThread extends Thread{ @Override public void run() { Random random=new Random(); while(true) { //打印当前线程名字 System.out.println(Thread.currentThread().getName()); try { //休眠几秒 Thread.sleep(random.nextInt(10)); }catch(InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { MyThread t1=new MyThread(); t1.start(); //启动线程,自动调用run() 方法 Random random=new Random(); while(true) { // main 方法本身运行在主线程 ( main线程) System.out.println(Thread.currentThread().getName()); try { Thread.sleep(random.nextInt(10)); } catch (InterruptedException e) { e.printStackTrace(); } } } }
线程概念
1)每个线程都是独立执行流;多个线程并发 执行
(操作系统快速切换,看起来同时运行)
2)这里一共两个线程:
a)main主线程:执行 main 函数里的 while 循环
b)t1自定义子线程:执行 MyThread 里run()的 while 循环
3)注意:调用 t1.start() 才是创建并启动线程;
不能直接调用 t1.run (),直接调用 run 只是普通方法调用,不会创建新线程Thread.sleep()
1)让当前线程休眠指定毫秒,进入阻塞状态
2)会抛出受检异常InterruptedException,必须 try-catch 捕获
3)random.nextInt(10)生成[0,9]随机整数,单位秒Thread.currentThread().getName()
1)获取当前正在执行代码的线程名称。
a)子线程默认名字:Thread-0
b)主线程名字:main
2)程序输出会交替打印 main 和 Thread-0,顺序随机,就是并发效果易错点
1)start():创建新线程,由 JVM 自动调用run()开启多线程
2)run():线程要执行的任务代码,直接调用只是普通方法,不会新建线程创建线程的 5 种方式
方式 1:继承 Thread 类
//继承Thread 方式创建线程 public static class MyThread extends Thread { // 重写run 方法:线程执行任务 @Override public void run() { System.out.println("这里是线程运行的代码"); } } public static void main(String[] args) { // 创建自定义线程 MyThread t=new MyThread(); t.start(); //启动线程,自动调用run方法 }要点:
1)run():线程任务逻辑 ,写线程要执行的代码
2)start():启动线程 ,向操作系统申请创建线程,然后自动执行 run ();不能直接调用 run () ,直接调用只是普通方法,不会创建新线程
3)在类内部this代表当前线程对象
方式 2:实现 Runnable 接口
// 实现Runnable 接口 public static class MyRunnable implements Runnable{ //重写 run 方法 定义线程任务 @Override public void run() { System.out.println("这里是线程运行的代码"); } } public static void main(String[] args) { // 创建任务对象 MyRunnable myRunnable =new MyRunnable(); //将任务对象传入Thread 构造器 ,创建线程对象 Thread t1=new Thread(myRunnable); t1.start(); // 调用start 启动线程 }要点:
1)Java 是单继承,如果类已经继承别的类,就只能用 Runnable 接口
2)this代表 MyRunnable 对象,不是线程对象 ;
获取当前线程对象要用Thread.currentThread()
2. 两种方式对比
简化写法(匿名内部类 + Lambda)
匿名内部类创建 Thread 子类
public static void main(String[] args) { // 匿名内部类:继承Thread ,重写run方法 Thread t1=new Thread() { @Override public void run() { System.out.println("使用匿名内部类创建Thread 子类对象"); } }; // 启动线程 t1.start(); }1)new Thread(){ ... }
a)这是匿名内部类,相当于快速创建一个继承Thread的子类,类没有名字。
b)大括号{}里面重写父类 Thread 的run()方法,run()存放线程要执行的任务。
2)t1.start()
a)调用start(),JVM 会向操作系统申请创建新线程,线程就绪后自动执行run()。
b) 如果写t1.run(),只是普通方法调用,不会开启新线程。
匿名内部类创建 Runnable
public static void main(String[] args) { //匿名内部类实现Runnable接口,作为Thread的任务参数 Thread t2=new Thread(new Runnable() { @Override public void run() { System.out.println("使用匿名内部类创建 Runnable 子类对象"); } }); t2.start(); }1)new Runnable(){ ... }:
Runnable 是接口,这里创建匿名内部类实现 Runnable 接口,
重写唯一抽象方法run(),run()里面就是线程要执行的任务。
2)把这个匿名 Runnable 实例传入 Thread 构造方法,
Thread 内部会持有这个任务对象。
3)调用 t2.start()开启新线程,线程调度后自动执行 Runnable 里面的run()。
Lambda 表达式
1)Runnable 是函数式接口(只有一个抽象方法),可以用 lambda 简化
public static void main(String[] args) { // 单行写法:lambda 表达式 Thread t3=new Thread(()-> System.out.println("lambda创建线程")); // 多行写法:需要大括号包裹 Thread t4=new Thread(()-> { System.out.println("lambda多行写法"); System.out.println("第二行任务代码"); }); // 启动线程 t3.start(); t4.start(); }2)原理:Runnable 是函数式接口,只有一个抽象方法 run(),
所以支持 Lambda 表达式简写。
a)() -> 代码 就代表实现 Runnable 的 run 方法。
b)():run 方法没有参数
c)->:箭头符号,分隔参数和方法体
3)两种 lambda 格式
a)单行语句:可以省略大括号和 return
() -> System.out.println("lambda创建线程")a)多条执行语句:必须加上 {}
() -> { System.out.println("第一行"); System.out.println("第二行"); }4)本质等价:
// lambda等价于前面的匿名Runnable Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println("匿名Runnable"); } });总结
1)
start()和run()的区别a)
run():只是普通方法,里面放任务代码,直接调用不会开启新线程b)start():native 方法,真正在 OS 创建线程,线程就绪后自动调用 run ()
2)推荐优先使用 Runnable + Lambda :解耦、不占用继承、代码简洁
3)线程启动只能调用
start(),一个线程对象只能 start 一次,多次 start 抛异常
多线程的优势-增加运行速度
Thread 类
- Thread 是 JVM 用来管理线程的类:
操作系统里每一个线程执行流,在 Java 中都会对应一个唯一的Thread对象- Thread对象就相当于线程的 "档案卡片",里面记录线程信息:
ID:线程 id
1)线程名称(姓名)
2)优先级
3)线程状态(就绪 / 运行 / 阻塞等)
4)其他属性- JVM 会维护这些
Thread对象,完成线程调度、线程管理 ,来决定 CPU 什么时候执行哪个线程
Thread 常见构造方法
这是 Java 多线程中Thread类的构造器,用来创建线程对象,
注意:new Thread () 只是创建线程对象,调用.start()才会真正启动线程







