01-异常、多线程
1、异常
1.1、异常概述
1.1.1、异常介绍和体系
- 异常就是程序可能出现了不正常的情况。程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止
- demo
- Error
- 严重问题,通过代码无法处理。比如:电源断了
- Exception
- 称为异常,它表示程序本身可以处理的问题
1.1.2、异常体系
- Exception编译时异常就是在编译的时候出现的异常
- RuntimeException运行时异常,就是在运行的时候出现的异常
- 虚拟机默认处理异常的方式
- 打印异常信息,终止程序
1.2、异常的处理方式
1.2.1、throws抛出异常
- throws格式
- 修饰符 返回值类型 方法名(参数) throws 异常类1,异常类2{}
- throws的作用
- 抛出异常,把异常抛给调用者处理
1.2.2、try...catch捕获异常
-
try...catch格式
-
try {
可能出现异常的代码
}catch(异常类名 变量名) {
异常的处理代码;
}
-
-
好处
- 可以让程序继续往下执行
1.2.3、finally代码块
-
格式
-
try {
可能出现异常的代码
}catch(异常类名 变量名) {
异常的处理代码;
}finally {
代码;(该代码一定会被执行)
}
-
1.2.4、Throwable的成员方法
- public String getMessage()返回此throwable的详细消息字符串
- public String toString()返回此可抛出的简短描述
- public void printStackTrace() 把异常的错误信息输出在控制台
1.2.5、注意事项
- 子类重写方法throws的异常要比父类方法throws的异常相同或更少
- 父类方法没有throws异常,子类方法只能try...catch处理
- PS:
- 这里的异常指的是编译异常
1.3、自定义异常
- 概念
- 就是为了让控制台的报错信息更加的见名知意
2、线程
2.1、多线程的好处
- 让程序可以"同时"做多件事情
2.2、并发和并行
- 并发
- 多个事件在同一时刻,同时执行
- 并行
- 多个事件在同一时刻,交替执行
2.3、进程和线程
- 进程概念
- 是一个正在运行的程序
- 线程概念
- 线程就是来执行代码的
- PS:线程是进程的执行单元
2.4、多线程运行原理
- CPU在多个线程间快速切换,造成"同时"运行的假象
- PS:本质上是交替执行,只是交替的很快,让用户感觉是同时执行
2.5、多线程的实现方式
2.5.1、继承Thread类
-
概述
- Thread类表示线程,通过Thread类启动多个线程
-
格式
-
// 1.定义类继承Thread类 public class MyThread extends Thread { // 2.重写run方法, 编写新线程要执行的代码 @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("run: " + i); } } } // 3.创建子类对象
MyThread mt = new MyThread();// 4.调用start()方法
mt.start(); // 启动新线程,新的线程会自动执行mt.run()方法
-
-
Thread类启动线程步骤
-
- 定义类继承Thread类
-
- 重写run方法,编写新线程要执行的代码
-
- 创建子类对象
-
- 调用star()方法
-
-
注意
- 多线程程序随机并发执行
-
优缺点
- 优点
- 编码简单
- 缺点
- 线程类已经继承Thread,无法继承其他类,不利于扩展
- 优点
2.5.2、run()方法和start()方法的区别
- 为什么要重写run()方法
- 因为run()方法是用来封装被线程执行的代码
- run()方法和start()方法的区别
- run方法是继承线程封装执行的代码,直接调用,相当于普通方法的调用,并没有开启线程
- start方法是启动新线程,新线程自动调用run()方法
- 为什么要先启动子线程,再执行主线程任务
- 避免主线程任务提前执行完毕了
2.5.3、实现Runnable接口
-
Thread构造器
- public Thread(Runnable target)(其中之一)
- 根据Runnable对象创建线程对象
- public Thread(Runnable target)(其中之一)
-
格式
-
MyRunnble r = new MyRunnble(); Thread t = new Thread(r); t.start(); for (int i = 0; i < 20; i++) { System.out.println("main" + i); }
-
-
实现Runnable接口启动线程步骤
-
- 定义一个类MyRunnable实现Runnable接口
-
- 在MyRunnable类中重写run()方法
-
- 创建MyRunnable类的对象
-
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
-
- 调用start()方法启动线程
-
-
优缺点
- 优点:可以继续继承类和实现接口,扩展性强
- 缺点:代码复杂一点,需要创建两个对象
2.5.4、匿名内部类创建线程
-
Demo
-
public class Demo06 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println("我是新线程!"); } } }).start(); while (true) { System.out.println("我是主线程!"); } } }
-
3、线程类的常见方法
3.1、获取和设置线程名称
-
方法
- String getName(): 获取线程的名字
- void setName(String name):设置线程的名字
- String getName(): 获取线程的名字
-
构造器
- Thread(String name) : 创建线程对象时,设置线程的名称
- Thread(Runnable r, String name) : 创建线程对象的时候,设置线程的名称
3.2、获得当前线程的对象
- 方法
- public static Thread currentThread():返回执行当前代码的线程对象
- 注意事项
- 此方法是Thread类的静态方法,可以直接使用Thread类调用
- 这个方法是被哪个线程执行的,就会得到哪个线程的对象
3.3、线程休眠
- 方法
- public static void sleep(long time):让线程休眠指定的事件,单位为毫秒
- 格式
- Thread.sleep(1000); // 休眠一秒
4、线程的安全问题
4.1、概念
- 多个线程同时操作共享资源导致数据错乱,称为线程安全问题
4.2、原因
- 多线程并发
- 同时访问共享资源
- 存在修改共享资源
4.3、多线程取钱业务案例
package Day08._02多线程.demo09多线程卖票; class SaleTicket00 extends Thread{ private static int TicketCount = 100; @Override public void run() { while (true) { if (TicketCount > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { } TicketCount --; System.out.println(getName() + "正在买票.........,剩余" + TicketCount + "张票"); } else { // System.out.println("票已经卖完了!"); break; } } } } public class Demo09 { public static void main(String[] args) { SaleTicket00 buy1 = new SaleTicket00(); buy1.setName("buy1"); buy1.start(); SaleTicket00 buy2 = new SaleTicket00(); buy2.setName("buy2"); buy2.start(); SaleTicket00 buy3 = new SaleTicket00(); buy3.setName("buy3"); buy3.start(); } }
4.4、线程同步思想
- 加锁
- 把共享资源进行上锁,每次只能一个线程进入访问,访问完毕以后解锁,然后其他线程才能进来
4.5、线程同步的实现方式
4.5.1、同步代码块
4.5.1.1、格式
- synchronized (锁对象)
4.5.1.2、作用
- 给代码块中的代码加锁,解决线程安全问题。
4.5.1.3、原理
- 每次只能一个线程获取锁进入,执行完毕以后自动解锁,其他线程才可以进来执行
4.5.1.4、要求
- 原则上:锁对象必须是同一个对象。
- 语法上:任意对象都可以作为锁,建议使用共享资源作为锁对象。
- 对于实例方法建 议使用this作为锁对象,此时this应该代表共享资源对象。
- 对于静态方法建议使用字节码类名.class对象作为锁对象。
4.5.2、同步方法
4.5.2.1、格式
- public synchronized void 方法名()
4.5.2.2、作用
- 给方法加锁,解决线程安全问题。
4.5.2.3、同步方法原理
- 每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
4.5.2.4、同步方法底层原理
- 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法。
- 如果方法是实例方法:同步方法默认用this作为的锁对象。
- 如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
4.5.3、Lock锁
4.5.3.1、API
4.5.3.2、格式
在成员变量位置: private final Lock lock = new ReentrantLock(); try { lock.lock() 操作共享资源的代码 } finally { lock.unlock(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?