07.异常、多线程、Lambda 表达式
一、异常
- 指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
- 异常体系
- 根类
- java.lang.Throwable
- 两个直接子类
- java.lang.Error
- 严重错误Error,无法通过处理的错误,只能事先避免。
- java.lang.Exception
- 表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。
- 这是我们平时所说的异常。
- java.lang.Error
- 两个直接子类
- java.lang.Throwable
- 根类
二、异常的处理
- 五个关键字:try、catch、finally、throw、throws。
- throw
- 可以使用 throw 关键字在指定的方法中抛出指定的异常。
- 格式
- throw new xxxException("异常产生的原因");
- 注意事项
- throw 关键字必须写在方法的内部。
- throw 关键字后边 new 的对象必须是 Exception 或者 Exception 的子类对象。
- throw 关键字抛出指定的异常对象,我们就必须处理这个异常对象。
- throw关键字后边创建的是 RuntimeException 或者是 RuntimeException 的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)
- throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try...catch。
- throws
- 异常处理的第一种方式,交给别人处理。
- 作用
- 当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象。
- 可以使用 throws 关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理-->中断处理。
- 格式:在方法声明时使用。
- 修饰符 返回值类型 方法名(参数列表) throws 异常类名1,异常类名2...{}
- 注意事项
- throws 关键字必须写在方法声明处。
- throws 关键字后边声明的异常必须是 Exception 或者是 Exception 的子类。
- 方法内部如果抛出了多个异常对象,那么 throws 后边必须也声明多个异常。
- 如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可。
- 调用了一个声明抛出异常的方法,我们就必须的处理声明的异常。
- 要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM。
- 要么 try...catch 自己处理异常。
- try..catch
- 处理异常的第二种方式,自己处理异常。
- 格式
- try{
- 可能产生异常的代码
- }catch(异常类型 变量名){
- 处理异常
- 一般在工作中,会把异常的信息记录到一个日志中。
- 处理异常
- }
- ...
- catch(异常类型 变量名){
- }
- try{
- 注意事项
- try 中可能会抛出多个异常对象,那么就可以使用多个 catch 来处理这些异常对象。
- 如果 try 中产生了异常,那么就会执行 catch 中的异常处理逻辑,执行完毕 catch 中的处理逻辑,继续执行 try...catch 之后的代码。
- 如果 try 中没有产生异常,那么就不会执行 catch 中异常的处理逻辑,执行完 try 中的代码,继续执行 try...catch 之后的代码。
- finally 代码块
- 格式
- try{
- 可能产生异常的代码
- }catch(异常类型 变量名){
- 处理异常
- 一般工作中,会把异常的信息记录到一个日志中。
- 处理异常
- }
- ...
- catch(异常类型 变量名){
- }finally{
- 无论是否出现异常都会执行。
- }
- try{
- 注意事项
- finally不能单独使用,必须和 try 一起使用。
- finally 一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放 (IO) 。
- 格式
- 异常注意事项
- 多个异常使用捕获如何处理
- 多个异常分别处理。
- 多个异常一次捕获,多次处理。
- 我们常用的方式,处理时需要注意:
- catch 里边定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上边,否则就会报错。
- 多个异常一次捕获一次处理。
- 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
- 默认给虚拟机处理,终止程序。
- 如果 finally 有 return 语句,永远返回 finally 中的结果,避免该情况。
- 如果父类抛出多个异常,子类复写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
- 父类方法没有抛出异常,子类重写父类方法时也不可抛出异常。
- 此时子类产生异常,只能捕获处理,不能声明抛出。
- 多个异常使用捕获如何处理
- 自定义异常
- Java 提供的异常类,不够我们使用,需要自己定义一些异常类。
- 格式
- public class XXXExcepiton extends Exception | RuntimeException{
- 添加一个空参数的构造方法
- 添加一个带异常信息的构造方法
- 查看源码发现,所有的异常类都会有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理这个异常信息。
- }
- public class XXXExcepiton extends Exception | RuntimeException{
- 注意事项
- 自定义异常类一般都是以 Exception 结尾,说明该类是一个异常类。
- 自定义异常类,必须的继承 Exception 或者 RuntimeException 。
- 继承 Exception
- 自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么 throws,要么 try...catch 。
- 继承 RuntimeException
- 自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)。
- 继承 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 相同。
- 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 类
- 第二种方式
- 实现 Runnable 接口。
- 实现步骤
- 创建一个 Runnable 接口的实现类。
- 在实现类中重写 Runnable 接口的 run 方法,设置线程任务。
- 创建一个 Runnable 接口的实现类对象。
- 创建 Thread 类对象,构造方法中传递 Runnable 接口的实现类对象。
- 调用 Thread 类中的 start 方法,开启新的线程执行 run 方法。
- 实现步骤
- 实现 Runnable 接口创建多线程程序的好处。
- 避免了单继承的局限性
- 增强了程序的扩展性,降低了程序的耦合性(解耦)。
- 实现 Runnable 接口的方式,把设置线程任务和开启新线程进行了分离(解耦)。
- 实现类中,重写了run方法:用来设置线程任务。
- 创建 Thread 类对象,调用start方法:用来开启新线程。
- 第一种方式
- 匿名内部类实现线程的创建
- 格式
- new 父类/接口(){
- 重复父类/接口中的方法
- }
- new 父类/接口(){
- 格式
四、线程安全
- 通过模拟多窗口卖票引出线程安全问题。
- 卖出不存在的票和重复的票。
- 解决线程安全问题的方案
- 使用同步代码块
- 格式
- synchronized (锁对象){
- 能会出现线程安全问题的代码(访问了共享数据的代码)
- }
- synchronized (锁对象){
- 同步代码块中的锁对象,可以使用任意的对象。
- 但是必须保证多个线程使用的锁对象是同一个。
- 锁对象作用
- 把同步代码块锁住,只让一个线程在同步代码块中执行。
- 格式
- 使用同步方法
- 使用步骤
- 把访问了共享数据的代码抽取出来,放到一个方法中。
- 在方法上添加 synchronized 修饰符。
- 格式
- 修饰符 synchronized 返回值类型 方法名(参数列表){
- 可能会出现线程安全问题的代码(访问了共享数据的代码)
- }
- 修饰符 synchronized 返回值类型 方法名(参数列表){
- 隐含的锁对象是谁
- 非 static 方法
- 谁调用这个方法,锁对象就是谁。
- 也就是 this 。
- 谁调用这个方法,锁对象就是谁。
- satic 方法
- 不能是 this
- this 是创建对象之后产生的,静态方法优先于对象。
- 是当前方法所在类的字节码对象(类名.class)。
- 不能是 this
- 非 static 方法
- 使用步骤
- 使用 Lock 锁
- java.util.concurrent.locks.Lock接口
- Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
- Lock 接口中的方法
- void lock()
- 获取锁。
- void unlock()
- 释放锁。
- void lock()
- 实现步骤
- 在成员位置创建一个 ReentrantLock 对象。
- 在可能会出现安全问题的代码前调用 Lock 接口中的方法 lock 获取锁。
- 在可能会出现安全问题的代码后调用 Lock 接口中的方法 unlock 释放锁。
- java.util.concurrent.locks.Lock接口
- 使用同步代码块
五、线程状态
- Timed Waiting (计时等待)
- 一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。
- 进入到 TimeWaiting(计时等待) 有两种方式
- 使用sleep(long m) 方法
- 在毫秒值结束之后,线程睡醒进入到 Runnable/Blocked 状态。
- 使用wait(long m) 方法
- 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 是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。
- 如何保证线程间通信有效利用资源
- 多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。 就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。
- 概念
- 等待唤醒机制
- 就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll() 来唤醒所有的等待线程。
- wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中。
- notify:选取所通知对象的 wait set 中的一个线程释放。
- notifyAll:则释放所通知对象的 wait set 上的全部线程。
- 注意
- 哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用 wait 方法之后的地方恢复执行。
- 注意
- 总结如下
- 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;
- 否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态。
- 细节
- wait方法与notify方法必须要由同一个锁对象调用
- 因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的 wait 方法后的线程。
- wait方法与notify方法是属于Object类的方法的。
- 因为:锁对象可以是任意对象,而任意对象的所属类都是继承了 Object 类的。
- wait方法与notify方法必须要在同步代码块或者是同步函数中使用
- 因为:必须要通过锁对象调用这2个方法。
- wait方法与notify方法必须要由同一个锁对象调用
- 就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll() 来唤醒所有的等待线程。
- 生产者消费者
七、线程池
- 就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
- java.util.concurrent.Executor
- 线程池的顶级接口
- 严格意义上讲并不是一个线程池,只是一个执行线程的工具。
- 真正的线程池接口是 java.util.concurrent.ExecutorService。
- 线程池的顶级接口
- java.util.concurrent.Executors
- 线程池的工厂类,用来生成线程池。
- static ExecutorService newFixedThreadPool(int nThreads)
- 创建一个可重用固定线程数的线程池。
- int nThreads
- 创建线程池中包含的线程数量。
- 返回值
- 返回的是 ExecutorService 接口的实现类对象,我们可以使用 ExecutorService 接口来接收。
- 面向接口编程。
- 获取到了一个线程池 ExecutorService 对象,如何使用。
- public Future<?> submit(Runnable task)
- 获取线程池中的某一个线程对象,并执行。
- Future 接口
- 用来记录线程任务执行完毕后产生的结果。
- void shutdown()
- 关闭/销毁线程池。
- public Future<?> submit(Runnable task)
- static ExecutorService newFixedThreadPool(int nThreads)
- 线程池使用步骤
- 使用线程池的工厂类 Executors 里边提供的静态方法 newFixedThreadPool 生产一个指定线程数量的线程池。
- 创建一个类,实现Runnable接口,重写run方法,设置线程任务。
- 调用 ExecutorService 中的方法 submit ,传递线程任务(实现类),开启线程,执行run方法。
- 调用 ExecutorService 中的方法 shutdown 销毁线程池(不建议执行)。
- 线程池都没有了,就不能获取线程了。
- 线程池的工厂类,用来生成线程池。
八、Lambda 表达式
- 引入
- 传统通过匿名内部类创建多线程方式
- 部分代码
- new Thread(new Runnable(){
- @Override
- public void run() {
- System.out.println(Thread.currentThread().getName()+" 新线程创建了"
- }
- }).start();
- new Thread(new Runnable(){
- 使用Lambda表达式写法
- new Thread(()->{
- System.out.println(Thread.currentThread().getName()+" 新线程创建了");
- }
- ).start();
- new Thread(()->{
- 优化省略Lambda写法
- new Thread(()->System.out.println(Thread.currentThread().getName()+" 新线程创建了")).start();
- 部分代码
- 传统通过匿名内部类创建多线程方式
- 2014年3月 Oracle 所发布的 Java 8(JDK 1.8)中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。
- Lambda表达式的标准格式
- 由三部分组成
- 一些参数
- 一个箭头
- 一段代码
- 格式
- (参数列表) ->{一些重写的方法}
- 解释
- ()
- 接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔。
- ->
- 传递的意思,把参数传递给方法体 {}。
- {}
- 重写接口的抽象方法的方法体。
- ()
- 由三部分组成
- 省略格式
- Lambda表达式:是可推导,可以省略。
- 凡是根据上下文推导出来的内容,都可以省略书写。
- 可省略的内容
- 参数列表
- 括号中参数列表的数据类型,可以省略不写。
- 括号中的参数如果只有一个,那么类型和 () 都可以省略。
- 一些代码
- 如果 {} 中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
- 注意:要省略 {},return。分号必须一起省略
- 如果 {} 中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
- 参数列表
- Lambda表达式:是可推导,可以省略。
- Lambda表达式的标准格式
九、异常、多线程、Lambda 表达式完结
07.异常、多线程、Lambda 表达式的更多相关文章
- Java 多线程 -- lambda 表达式推导
jdk 8 开始 java 引入了lambda 表达式. lambda适用场景: 1.接口或父类 2.接口或父类只有一个方法 我们从多线程写法来推导一下: 1.外部类写法: package com.x ...
- 转:【More Effective C#】Lambda表达式优化
http://www.cnblogs.com/kongyiyun/archive/2010/10/19/1855274.html 使用Lambda表达式将会造成Lambda表达式主题部分的代码重复. ...
- Lambda表达式 简介 语法 示例
Lambda 表达式也称为闭包,是匿名类的简短形式.Lambda 表达式简化了[单一抽象方法声明接口]的使用,因此 lambda 表达式也称为功能接口. 在 Java SE 7 中,单一方法接口可使用 ...
- Lambda 表达式的示例-来源(MSDN)
本文演示如何在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述,请参阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的详细信息,请参阅 Lambda 表 ...
- Lambda 表达式的示例
本文中的过程演示如何使用 lambda 表达式. 有关 lambda 表达式的概述,请参见 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的更多信息,请参见 Lambda 表达式 ...
- Lambda表达式 简介 语法 示例 匿名内部类
在AS中使用 Lambda 表达式 Demo地址:https://github.com/baiqiantao/MultiTypeTest.git Gradle(Project级别)中添加classpa ...
- Lambda 表达式的演示样例-来源(MSDN)
本文演示怎样在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述.请參阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的具体信息,请參阅 Lambda 表 ...
- Java8新特性(1):Lambda表达式
Lambda表达式可以理解为一种匿名函数:没有名称,但有参数列表.函数主体.返回类型.它是行为参数化的一种实现,行为参数化是指将不同的行为作为参数传递给方法,方法的所具备的能力取决于它接收的行为参数. ...
- 01 语言基础+高级:1-7 异常与多线程_day07 【线程池、Lambda表达式】
day07[线程池.Lambda表达式] 主要内容 等待与唤醒案例 线程池 Lambda表达式 教学目标 -[ ] 能够理解线程通信概念-[ ] 能够理解等待唤醒机制-[ ] 能够描述Java中线程池 ...
- C#多线程+委托+匿名方法+Lambda表达式
线程 下面是百度写的: 定义英文:Thread每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程.进程也可能是整个程序或者是部分程序的动态执行.线程是一组指令的集合,或者是程序的特殊段,它 ...
随机推荐
- koa源代码解析
koa不愧为小而美,主要代码很少.简单来说,1,koa封装了node的http.createServer((req,res)=>{})的入参req,res到ctx同名属性(一个自定义对象)中,并 ...
- 打包exe
2.要打包的文件为多个py文件 这种情况一般你的代码较多,项目较大,可能你写了一个GUI界面py文件这个文件调用了其他文件的函数什么的,这个时候你需要生成spec文件来打包,这里假设你的要打包的主文件 ...
- SFINAE几种实现方式
一.通过函数返回值实现 template<class T> typename std::enable_if<std::is_trivially_default_constructib ...
- Git 工作常用操作
撤回commit 上一次提交的代码 git reset --soft HEAD^ HEAD^的意思是上一个版本,也可以写成HEAD~1 如果你进行了2次commit,想都撤回,可以使用HEAD~2 g ...
- v4l2编程
一.video 4 linux 2 ,是linux中关于视频设备的内核驱动.在linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video 0下 二一般操作流程( ...
- FTP为什么越来越不好用了?要如何替代?
FTP相信很多人都不陌生,作为世界范围内第一个文件传输协议,FTP解决了互联网文件传输需求,至今已被广泛使用30多年.但很多人现在慢慢发现,FTP越来越不好用了,或者说越来越无法满足自己需求了,这是为 ...
- JS学习-Canvas
Canvas Canvas API 提供了一个通过JavaScript 和 HTML的<canvas>元素来绘制图形的方式.它可以用于动画.游戏画面.数据可视化.图片编辑以及实时视频处理等 ...
- promethus+grafana监控
1.监控 MySQL (终端可以安装在任意主机,不一定按在mysql节点上,注:mysql版本需在5.5以上) I.首先在mysql中添加监控使用的用户: create user 'exp'@'%' ...
- el-table 如果文字过多展示...
1 <el-table-column label="任务名称" width="120px" align="center" :show- ...
- MySQL-explain详解说明
1.Explain介绍 在日常工作中, 我们会有时会开慢查询去记录一些执行时间比较久的SQL语句, 找出这些SQL语句后我们常常会用explain这个命令来查看一个这些SQL语句的执行计划, 查看该S ...