一、异常
- 指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
- 异常体系
- 根类
- java.lang.Throwable
- 两个直接子类
- java.lang.Error
- 严重错误Error,无法通过处理的错误,只能事先避免。
- 严重错误Error,无法通过处理的错误,只能事先避免。
- java.lang.Exception
- 表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。
- 这是我们平时所说的异常。
- 表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。
- java.lang.Error
- 两个直接子类
- java.lang.Throwable
- 根类
二、异常的处理
- 五个关键字:try、catch、finally、throw、throws。
- throw
- 可以使用 throw 关键字在指定的方法中抛出指定的异常。
- 格式
- throw new xxxException("异常产生的原因");
- throw new xxxException("异常产生的原因");
- 注意事项
- throw 关键字必须写在方法的内部。
- throw 关键字后边 new 的对象必须是 Exception 或者 Exception 的子类对象。
- throw 关键字抛出指定的异常对象,我们就必须处理这个异常对象。
- throw关键字后边创建的是 RuntimeException 或者是 RuntimeException 的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)
- throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try...catch。
- throw关键字后边创建的是 RuntimeException 或者是 RuntimeException 的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)
- throw 关键字必须写在方法的内部。
- throws
- 异常处理的第一种方式,交给别人处理。
- 作用
- 当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象。
- 可以使用 throws 关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理-->中断处理。
- 当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象。
- 格式:在方法声明时使用。
- 修饰符 返回值类型 方法名(参数列表) throws 异常类名1,异常类名2...{}
- 修饰符 返回值类型 方法名(参数列表) throws 异常类名1,异常类名2...{}
- 注意事项
- throws 关键字必须写在方法声明处。
- throws 关键字后边声明的异常必须是 Exception 或者是 Exception 的子类。
- 方法内部如果抛出了多个异常对象,那么 throws 后边必须也声明多个异常。
- 如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可。
- 如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可。
- 调用了一个声明抛出异常的方法,我们就必须的处理声明的异常。
- 要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM。
- 要么 try...catch 自己处理异常。
- 要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM。
- throws 关键字必须写在方法声明处。
- 异常处理的第一种方式,交给别人处理。
- try..catch
- 处理异常的第二种方式,自己处理异常。
- 格式
- try{
- 可能产生异常的代码
- }catch(异常类型 变量名){
- 处理异常
- 一般在工作中,会把异常的信息记录到一个日志中。
- 一般在工作中,会把异常的信息记录到一个日志中。
- 处理异常
- }
- ...
- catch(异常类型 变量名){
- }
- try{
- 注意事项
- try 中可能会抛出多个异常对象,那么就可以使用多个 catch 来处理这些异常对象。
- 如果 try 中产生了异常,那么就会执行 catch 中的异常处理逻辑,执行完毕 catch 中的处理逻辑,继续执行 try...catch 之后的代码。
- 如果 try 中没有产生异常,那么就不会执行 catch 中异常的处理逻辑,执行完 try 中的代码,继续执行 try...catch 之后的代码。
- try 中可能会抛出多个异常对象,那么就可以使用多个 catch 来处理这些异常对象。
- finally 代码块
- 格式
- try{
- 可能产生异常的代码
- }catch(异常类型 变量名){
- 处理异常
- 一般工作中,会把异常的信息记录到一个日志中。
- 处理异常
- }
- ...
- catch(异常类型 变量名){
- }finally{
- 无论是否出现异常都会执行。
- }
- try{
- 注意事项
- finally不能单独使用,必须和 try 一起使用。
- finally 一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放 (IO) 。
- finally不能单独使用,必须和 try 一起使用。
- 格式
- 异常注意事项
- 多个异常使用捕获如何处理
- 多个异常分别处理。
- 多个异常一次捕获,多次处理。
- 我们常用的方式,处理时需要注意:
- catch 里边定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上边,否则就会报错。
- 多个异常一次捕获一次处理。
- 多个异常分别处理。
- 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
- 默认给虚拟机处理,终止程序。
- 默认给虚拟机处理,终止程序。
- 如果 finally 有 return 语句,永远返回 finally 中的结果,避免该情况。
- 如果父类抛出多个异常,子类复写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
- 父类方法没有抛出异常,子类重写父类方法时也不可抛出异常。
- 此时子类产生异常,只能捕获处理,不能声明抛出。
- 多个异常使用捕获如何处理
- 自定义异常
- Java 提供的异常类,不够我们使用,需要自己定义一些异常类。
- 格式
- public class XXXExcepiton extends Exception | RuntimeException{
- 添加一个空参数的构造方法
- 添加一个带异常信息的构造方法
- 查看源码发现,所有的异常类都会有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理这个异常信息。
- 查看源码发现,所有的异常类都会有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理这个异常信息。
- 添加一个空参数的构造方法
- }
- public class XXXExcepiton extends Exception | RuntimeException{
- 注意事项
- 自定义异常类一般都是以 Exception 结尾,说明该类是一个异常类。
- 自定义异常类,必须的继承 Exception 或者 RuntimeException 。
- 继承 Exception
- 自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么 throws,要么 try...catch 。
- 自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么 throws,要么 try...catch 。
- 继承 RuntimeException
- 自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)。
- 自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)。
- 继承 Exception
- 自定义异常类一般都是以 Exception 结尾,说明该类是一个异常类。
三、多线程
- 并发与并行
- 并发
- 指两个或多个事件在同一时段内发生。
- 并行
- 指两个或多个事件在同一时刻发生(同时发生)。
- 并发
- 线程与进程
- 进程
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
- 线程
- 线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
- 线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
- 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
- Java 程序属于抢占式调度,那个线程的优先级高,那个线程优先执行;同一个优先级,随机选择一个执行。
- 进程
- Thread 类
- 构造方法
- public Thread();
- 分配一个新的线程对象。
- public Thread(String name)
- 分配一个带指定名字的新的线程对象。
- public Thread(Runnable target)
- 分配一个带有指定目标的新的线程对象。
- public Thread(Runnable target , String name)
- 分配一个带有指定目标的新的线程对象并指定名字。
- public Thread();
- 常用方法
- String getName()
- 返回该线程的名称。
- 返回该线程的名称。
- void setName(String name)
- 改变线程名称,使之与参数 name 相同。
- 改变线程名称,使之与参数 name 相同。
- static Thread currentThread()
- 返回对当前正在执行的线程对象的引用。
- 返回对当前正在执行的线程对象的引用。
- static void sleep(long millis)
- 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
- 毫秒数结束之后,线程继续执行。
- 毫秒数结束之后,线程继续执行。
- 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
- String getName()
- 构造方法
- 创建多线程
- 第一种方式
- 继承 Thread 类
- 实现步骤
- 创建一个 Thread 类的子类。
- 在 Thread 类的子类中重写 Thread 类中的 run 方法,设置线程任务(开启线程要做什么?)
- 创建 Thread 类的子类对象。
- 调用 Thread 类中的方法 start 方法,开启新的线程,执行 run 方法。
- void start()
- 使该线程开始执行。
- Java 虚拟机调用该线程的 run 方法
- 使该线程开始执行。
- void start()
- 创建一个 Thread 类的子类。
- 实现步骤
- 继承 Thread 类
- 第二种方式
- 实现 Runnable 接口。
- 实现步骤
- 创建一个 Runnable 接口的实现类。
- 在实现类中重写 Runnable 接口的 run 方法,设置线程任务。
- 创建一个 Runnable 接口的实现类对象。
- 创建 Thread 类对象,构造方法中传递 Runnable 接口的实现类对象。
- 调用 Thread 类中的 start 方法,开启新的线程执行 run 方法。
- 创建一个 Runnable 接口的实现类。
- 实现步骤
- 实现 Runnable 接口创建多线程程序的好处。
- 避免了单继承的局限性
- 增强了程序的扩展性,降低了程序的耦合性(解耦)。
- 实现 Runnable 接口的方式,把设置线程任务和开启新线程进行了分离(解耦)。
- 实现类中,重写了run方法:用来设置线程任务。
- 创建 Thread 类对象,调用start方法:用来开启新线程。
- 实现 Runnable 接口的方式,把设置线程任务和开启新线程进行了分离(解耦)。
- 第一种方式
- 匿名内部类实现线程的创建
- 格式
- new 父类/接口(){
- 重复父类/接口中的方法
- 重复父类/接口中的方法
- }
- new 父类/接口(){
- 格式
四、线程安全
- 通过模拟多窗口卖票引出线程安全问题。
- 卖出不存在的票和重复的票。
- 解决线程安全问题的方案
- 使用同步代码块
- 格式
- synchronized (锁对象){
- 能会出现线程安全问题的代码(访问了共享数据的代码)
- 能会出现线程安全问题的代码(访问了共享数据的代码)
- }
- synchronized (锁对象){
- 同步代码块中的锁对象,可以使用任意的对象。
- 但是必须保证多个线程使用的锁对象是同一个。
- 锁对象作用
- 把同步代码块锁住,只让一个线程在同步代码块中执行。
- 把同步代码块锁住,只让一个线程在同步代码块中执行。
- 格式
- 使用同步方法
- 使用步骤
- 把访问了共享数据的代码抽取出来,放到一个方法中。
- 在方法上添加 synchronized 修饰符。
- 把访问了共享数据的代码抽取出来,放到一个方法中。
- 格式
- 修饰符 synchronized 返回值类型 方法名(参数列表){
- 可能会出现线程安全问题的代码(访问了共享数据的代码)
- 可能会出现线程安全问题的代码(访问了共享数据的代码)
- }
- 修饰符 synchronized 返回值类型 方法名(参数列表){
- 隐含的锁对象是谁
- 非 static 方法
- 谁调用这个方法,锁对象就是谁。
- 也就是 this 。
- 谁调用这个方法,锁对象就是谁。
- satic 方法
- 不能是 this
- this 是创建对象之后产生的,静态方法优先于对象。
- this 是创建对象之后产生的,静态方法优先于对象。
- 是当前方法所在类的字节码对象(类名.class)。
- 不能是 this
- 非 static 方法
- 使用步骤
- 使用 Lock 锁
- java.util.concurrent.locks.Lock接口
- Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
- Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
- Lock 接口中的方法
- void lock()
- 获取锁。
- 获取锁。
- void unlock()
- 释放锁。
- 释放锁。
- void lock()
- 实现步骤
- 在成员位置创建一个 ReentrantLock 对象。
- 在可能会出现安全问题的代码前调用 Lock 接口中的方法 lock 获取锁。
- 在可能会出现安全问题的代码后调用 Lock 接口中的方法 unlock 释放锁。
- 在成员位置创建一个 ReentrantLock 对象。
- java.util.concurrent.locks.Lock接口
- 使用同步代码块
五、线程状态
- Timed Waiting (计时等待)
- 一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。
- 进入到 TimeWaiting(计时等待) 有两种方式
- 使用sleep(long m) 方法
- 在毫秒值结束之后,线程睡醒进入到 Runnable/Blocked 状态。
- 在毫秒值结束之后,线程睡醒进入到 Runnable/Blocked 状态。
- 使用wait(long m) 方法
- wait 方法如果在毫秒值结束之后,还没有被 notify 唤醒,就会自动醒来,线程睡醒进入到 Runnable/Blocked 状态。
- wait 方法如果在毫秒值结束之后,还没有被 notify 唤醒,就会自动醒来,线程睡醒进入到 Runnable/Blocked 状态。
- 使用sleep(long m) 方法
- 唤醒的方法
- void notify()
- 唤醒在此对象监视器上等待的单个线程。
- void notifyAll()
- 唤醒在此对象监视器上等待的所有线程。
- 唤醒在此对象监视器上等待的所有线程。
- void notify()
- BLOCKED (锁阻塞)
- 一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态。
- 线程A与线程B代码中使用同一锁,如果线程 A 取到锁,线程 A 进入到 Runnable 状态,那么线程 B 就进入到 Blocked 锁阻塞状态。
- 一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态。
- Waiting (无限等待)
- 一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。
- 如何进入
- void wait();
- 如何唤醒
- void notify()
- void notifyAll()
- 如何进入
- 一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的 Object.notify() 方法或 Object.notifyAll() 方法。
- 也就是等待唤醒机制。
- 一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。
六、等待唤醒机制
- 线程间通信
- 概念
- 多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。
- 多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。
- 为什么要处理线程间通信
- 多个线程并发执行时, 在默认情况下 CPU 是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。
- 多个线程并发执行时, 在默认情况下 CPU 是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。
- 如何保证线程间通信有效利用资源
- 概念
- 等待唤醒机制
七、线程池
- 就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
- java.util.concurrent.Executor
- 线程池的顶级接口
只是一个执行线程的工具。
- 线程池的顶级接口
-
-
- 引入
- 传统通过匿名内部类创建多线程方式
- 部分代码
- new Thread(new Runnable(){
- @Override
- public void run() {
- System.out.println(Thread.currentThread().getName()+" 新线程创建了"
- }
- @Override
- }).start();
- new Thread(new Runnable(){
- 使用Lambda表达式写法
- new Thread(()->{
- System.out.println(Thread.currentThread().getName()+" 新线程创建了");
- }
- System.out.println(Thread.currentThread().getName()+" 新线程创建了");
- ).start();
- new Thread(()->{
- 优化省略Lambda写法
- new Thread(()->System.out.println(Thread.currentThread().getName()+" 新线程创建了")).start();
- new Thread(()->System.out.println(Thread.currentThread().getName()+" 新线程创建了")).start();
- 部分代码
- 传统通过匿名内部类创建多线程方式
-
-
-
- 省略格式
- Lambda表达式:是可推导,可以省略。
- 凡是根据上下文推导出来的内容,都可以省略书写。
- 可省略的内容
- 参数列表
- 括号中参数列表的数据类型,可以省略不写。
- 括号中的参数如果只有一个,那么类型和 () 都可以省略。
- 括号中参数列表的数据类型,可以省略不写。
- 一些代码
- 如果 {} 中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
- 注意:要省略 {},return。分号必须一起省略
- 如果 {} 中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
- 参数列表
- 凡是根据上下文推导出来的内容,都可以省略书写。
- Lambda表达式:是可推导,可以省略。
-
九、异常、多线程、Lambda 表达式完结