Day5:异常 多线程
1:异常的分类及解释
1:异常继承体系为:异常的根类是 java.lang.Throwable,其下有两个子类:java.lang.Error 与 java.util.Exception 。而Exception又分为编译时期异常:checked异常,与运行时期异常:runtime异常。
2:Error:表示不可修复的恶性的错误,只能通过修改代码规避错误的产生,通常是系统级别的,所以很严重。
3:Exception:表示可修复的良性(相对于错误)的异常,异常产生后程序员可以并且应该通过代码的方式纠正,使程序继续运行,是必须要处理的。
4:运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。
2:异常的的处理方式
一:throws声明抛出异常
throws, 声明抛出 (方法自己不处理, 交给方法调用者处理, "甩锅给别人")
作用: 告诉方法的调用者, 调用此方法有可能发生异常, 需要在编写代码时处理
throw关键字作用: 在方法中制定的异常对象
格式:
throw new 异常类名("异常原因字符串"); 注意: new 的对象必须是"Exception" 或 "Excetion的子类" 的对象
throw和throws的区别
1:throw关键字通常用在方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即停止,它后面的语句都不执行。
2:throws关键字通常被应用在声明方法时,用来指定可能抛出的异常。多个异常可以使用逗号隔开。当在主函数中调用该方法时,如果发生异常,就会将异常对象抛给方法调用处
二:捕获异常
try...catch...finally是捕获异常,自己处理,处理完毕后面的程序可以继续运行
格式:
try {
// 可能产生异常的代码
} catch (异常类名 变量名) {
// 处理异常的代码
// 一般会将异常信息存储到日志中
}
...
} finally (异常类名 变量名) {
// 处理异常的代码
// 无论是否发生异常,都必须执行的代码块
}
3:Throwable中的3个异常处理方式java.lang.Throwable
// 成员方法
String getMessage(): 异常的信息. 没有原因返回null
String toString(): 异常的类型和原因信息
void printStackTrace(): 使用标准错误输出流打印异常信息(常用方式)
4:异常处理注意事项
1:异常存在继承关系,子类异常在上,父类异常在下,多个异常用Excption多态捕获
2:finally代码块中如果有return语句,返回的是return语句的值
3:如果父类抛出多个异常,①抛出和父类相同的异常 ②抛出父类异常的子类 ③不抛出异常
父类没有抛出异常,子类重写父类方法时也不能抛出异常,如果子类产生该异常,只能捕获处理
自定义异常类: ①定义编译时异常: 继承 Exception ②定义运行时异常: 继承RuntimeException
5:多线程
1:并发:(交替执行) 指两个或多个事件在同一时间段内发生 2:并行:(同时执行) 指两个或多个事件在同一时间发生
3:线程:代码执行的路径. 一个程序运行至少有一个进程,进程可以包含多个线程
多线程好处: 效率高, 多个线程之间互不影响
4:线程调度: ①分时调度:所有线程轮流使用cpu,平分占用cpu的时间 ②抢占式调度:优先级相同,随机选择一个线程执行
Day6:线程 同步 线程间通信
一:线程
1:Thread类
第一种方式:继承Thread类
1.定义一个子线程的类,继承Thread类;
2.在子线程类中重写run方法,
3.定义一个测试类;
4.在main方法中创建子线程对象;
5.调用子线程对象的start方法,开启子线程;
java.lang.Thread类: 表示线程.
// 成员方法
void run(): 用于让子类重写, 表示该线程要执行的任务.不能直接调用
void start(): 启动线程, 即让线程开始执行run()方法中的代码
String getName(): 获取线程的名称
void setName(String name): 设置线程名称
// 静态方法
static Thread currentThread(): 返回对当前正在执行的线程对象的引用
static void sleep(long millis): 让所在线程睡眠指定的毫秒
2:实现Runnable接口
第二种方式:实现Runnable接口
1.定义一个子任务类,实现Runnable接口;
2.在子任务类中重写run方法,
3.定义一个测试类;
4.在main方法中创建一个子任务对象;
5.在main方法中创建一个Thread类的对象,并把子任务对象传递给Thread类的构造方法;
6.调用Thread类对象的start方法开启子线程;
优势:
1:避免单继承的局限性 2增强程序的扩展性,降低程序的耦合性(解耦)
3:匿名内部类
public class Test {
public static void main(String[] args) {
// 匿名内部类方式
new Thread(new Runnable(){
@Override
public void run() {
// 要执行的任务
}
}).start();
}
}
4:线程安全的产生及解决方式
原因:多个线程操作共享资源,由于JVM是抢占式调度,一个线程没有执行完,另一个线程就来操作,就会出现问题
①:线程安全解决方式1:同步代码块
锁对象可以是任意类型对象,必须是被多个线程共享的唯一的对象, 作用: 只让一个程序在同步代码块中执行
格式:
synchronized (锁对象) {
// 操作共享数据的代码
}
②:线程安全解决方式2:同步方法
非静态方法的锁对象:this
// 非静态同步方法
public synchronized void method(){
// 可能会产生线程安全问题的代码
}
静态同步方法的对象:字节码对象 class对象
获取一个类的字节码对象的3种方式:
1. 对象名.getClass() new RunnableImpl().getClass()
2. 类名.class RunnableImpl.class
3. Class.forName("类的全名"); Class.forName("com.itheima.test05.RunnableImpl");
③:线程安全解决方式3:Lock锁
java.util.concurrent.locks.Lock接口:
使用方式:
public class RunnableImpl implements Runnable {
// 成员变量创建锁对象, 该锁对象也要所有线程共享唯一一个
Lock lock = new ReentrantLock(); // 成员变量
@Override
public void run() {
// 加锁
lock.lock();
try {
// 操作共享变量的代码...
} finally {
// 在finally中保证释放锁
lock.unlock();
}
}
}
二:线程间的通信
线程的生命周期中, 可以出现有6种状态:
1. "NEW 新建"
线程被创建, 但没有调用 start() 启动
2. "RUNNABLE 可运行"
调用 start()方法后已启动, 但可能正在执行 run() 方法的代码, 也可能正在等待CPU的调度
3. "BLOCKED (锁)阻塞"
线程试图获取锁, 但此时锁被其他线程持有
4. "WAITING 无限等待"
通过锁对象调用无参的 wait() 进入此状态.
等待其他线程通过锁对象执行 notify() 或 notifyAll() 才能结束这个状态
5. "TIMED_WAITING 计时等待"
如通过锁对象调用有参的 wait(long millis) 或 sleep(long millis), 则进入此状态.
直到时间结束之前被其他线程通过锁对象执行 notify()或 notifyAll()唤醒, 或时间结束自动唤醒
6. "TERMINATED 终止"
run()方法结束(执行结束, 或内部出现异常), 则进入此状态
(notify():随机唤醒一条线程 notifyAll():唤醒所有的线程 wait():让当前线程处于"无限等待"状态)通过锁对象调用
|
|