Java语言基础--进程与线程
概念
进程就是运行在系统中的代码集合(程序,它占用一定的内存和CPU资源,存在生存和消亡的动态过程
线程由进程创建,是进程的实体,一个进程可以有多个线程
并发:同一时刻,多个任务交替执行,貌似同时的错觉(单核CPU实现多任务就是并发)
并行:同一时刻,多个任务同时执行(多核CPU可以实现并行)
并行和并发可能同时存在
类图
使用
-
继承Thread类(源码解读)
public class Thread01 { public static void main(String[] args) throws InterruptedException { //创建Cat对象,可以当做线程使用 Cat cat = new Cat(); //启动线程-> 执行抽象方法start0->底层使用C实现根据不同系统多线程->根据Runnable接口原则来调用run方法 cat.start(); } } } //Cat类继承Thread类 class Cat extends Thread{ @Override public void run(){ //业务代码 } }
-
实现Runnable接口(源码解读:代理模式)
public class Thread02 { public static void main(String[] args) { Dog dog = new Dog(); Thread thread = new Thread(dog); thread.start(); /* Thread类使用了代理模式,内部使用以下代码来实现代理,原理上还是使用Thread类 private Runnable target = null;//属性,类型是 Runnable @Override public void run() { if (target != null) { target.run();//动态绑定(运行类型Dog) } } */ } } //通过实现Runnable接口,开发线程 class Dog implements Runnable { @Override public void run() { //业务代码 } }
多线程安全
编程模拟三个售票窗口,售票100,分别使用继承和实现方式来实现,发现线程间出现超卖现象
后面学习同步机制(Synchronized)可以解决以上问题
线程终止
-
线程完成任务后,自动终止
-
通过变量控制终止,即通知终止
public class ThreadExit_ { public static void main(String[] args) throws InterruptedException { T t1 = new T(); t1.start(); //如果希望main线程去控制t1 线程的终止, 必须可以修改 loop //让t1 退出run方法,从而终止 t1线程 -> 通知方式 //让主线程休眠 10 秒,再通知 t1线程退出 System.out.println("main线程休眠10s..."); Thread.sleep(10 * 1000); t1.setLoop(false); } } class T extends Thread { private int count = 0; //设置一个控制变量 private boolean loop = true; @Override public void run() { while (loop) { try { Thread.sleep(50);// 让当前线程休眠50ms } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("T 运行中...." + (++count)); } } public void setLoop(boolean loop) { this.loop = loop; } }
线程常用方法
-
setName : 设置线程名称,使之与参数name相同
-
getName:获取该线程名称
-
start:开启线程执行,Java虚拟机底层调用该线程的start0方法
-
run:底层调用的抽象接口方法,里面是具体的业务代码
-
setPriority:设置线程优先级(MAX=10,MIN=1,NORM=5)
-
getPriority:获取线程优先级
-
sleep:在指定毫秒数内让当前线程休眠(暂停),方法中可能会抛出InterruptedException异常,要明确继续抛或者捕捉
-
interrupt:中断线程,不是终止线程,一般用于中断线程休眠行为(主动抛出InterruptedException异常给线程)
-
yield:线程的礼让,让出cpu,让其他线程执行,但礼让的时间不确定,所以不一定礼让成功(静态方法,归属Thread类)
-
join:线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务(调用对方的join方法,让对方插队)
-
用户线程(又名工作线程)和守护线程
- 用户线程:当线程的任务执行完成自然结束或者已通知方式结束
- 使用start方法启动
- 守护线程:一般是为用户线程服务和管理,当所有的用户线程结束,守护线程自动结束(常见守护线程-垃圾回收机制)
- 先使用线程方法setDaemon(true)将线程属性标记为守护线程
- 使用start方法启动
- 用户线程:当线程的任务执行完成自然结束或者已通知方式结束
线程的生命周期
NEW: 尚未启动的线程处于此状态
RUNNABLE: 在Java虚拟机中执行的线程处于此状态
BLOCKED: 被阻塞等待监视器锁定的线程处于此状态
WAITING: 正在等待另一个线程执行特定动作的线程处于此状态
TIMED_WAITING: 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
TERMINATED: 已退出的线程处于此状态
同步机制(Synchronized)
在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就是用同步访问技术,保证数据在同一时刻,最多有一个线程访问,以保证数据的完整性,但会导致执行效率要降低
Java中引入了对象互斥锁,来保证共享数据操作的完整性,使用关键字synchronized来修饰对象来表明该对象在任意时刻只能由一个线程访问(颗粒理解为在对象身上寄存锁,当一个线程是抢到使用权限时会锁上,避免其他线程执行这段代码,其他线程都要等该线程使用完后开锁,才可以继续抢使用权,但是这个机制并不公平,可以让同一个线程一直抢到)
使用
- 同步代码块
synchronized (对象){ 得到对象的锁,才能操作同步代码
//需要被同步的代码;
}
- 同步方法
public synchronized void m(String name){
//需要被同步的代码
}
注意:
-
在同一时刻,只能有一个线程来执行被标记为同步的代码
-
非静态修饰锁是this(实例化对象),也可以匙其他对象(但必须是同一个对象),静态修饰锁是当前类本身(当前类名.class),也就是说,要让所有涉及的线程都使用同一把锁(这里的锁在同一对象上)才能保证同步机制正常运行
-
使用步骤:分析上锁的代码,选择同步代码块或者同步方法,要求多线程的锁对象为同一个即可
死锁
要避免此现象,多个线程互相占用对方锁,不肯相让,导致死锁
演示核心代码
释放锁
下面操作会释放锁
- 当前线程的同步代码执行结束
- 当前线程在同步代码中遇到break、return
- 当前线程在同步代码中出现了未处理的Error或Exception,导致异常结束
- 当前线程在同步代码中执行了线程对象的wait()方法,当前线程暂停,并释放锁
下面操作不会释放锁
- 线程执行同步代码中执行了Thread.sleep()、Thread.yield()暂停线程的执行,不会释放锁
- 线程执行同步代码时,其他线程调用了该线程的suspend()方法来挂起该线程,并不会释放锁(应该避免使用suspend、resume,它们已被弃用)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?