当前位置:   article > 正文

5.Java学习笔记:异常的捕获,抛出,处理;线程概念,状态,安全;多线程,线程池;等待与唤醒机制,lambda表达式。_runnable抛出异常和捕获异常的区别

runnable抛出异常和捕获异常的区别

第七章 异常

1. 异常概念

  1. 异常概念

    • 异常是一个类,处理异常:中断处理。
    • 异常不是语法错误,语法错误编译不通过。
  2. 异常体系

  • 异常的所有父类是java.lang.throwable,其下有两个子类
    • java.lang.Errorjava.lang.Exception,平常所说的异常是后者。
    • error是非常严重的问题,叫错误,只能规避,无法处理,尽量避免;
    • exception,可以处理。
  1. 异常分类
    • exception编译期异常,写代码时异常。
      • RuntimeException:运行期异常,java运行过程中出现的问题。
    • Error:错误,必须修改代码,程序才能运行。
  2. 异常产生过程解析:
    • 方法创建异常对象(名称,原因,位置)–>抛出给main方法,在抛出给–>控制台。

2. 异常的处理

2.1抛出异常 throw

throw关键字

  • 使用throw在指定的方法中抛出指定异常

  • 使用格式:throw new xxxException("异常产生的原因")

  • throw关键字必须写在方法的内部

  • throw关键字后new的对象必须是Exception或者Exception的子类对象。

  • throw关键字抛出指定的异常对象,我们就必须处理这个异常对象。

    throw关键字后面创建的是RuntimeException或者它的子类对象可以不处理,交给JVM处理

    throw后创建的是编译异常(写代码时的异常),则必须处理这个异常,要么throws,要么try…catch

  • 必须对方法传递到参数进行合法性校验,如果参数不合法,必须告知调用者传递的参数有问题。

2.2Objects非空判断

对传递参数的合法性判断,判断是否为null,

Objects.requireNonNull(obj,"传递的时空null");

2.3声明异常 throws

  • 异常处理的第一种方法:交给别人处理。

    • 作用:方法内部抛出异常时,就必须处理这个异常对象。最终交给JVM处理。

    • 使用格式:在方法声明时使用

      修饰符 返回值类型 方法名(参数裂变) throws xxxException, xxxException{

      ​ throw new XXXException(“产生原因”);

      }

    • throws关键字必须写在主方法声明处,

    • throws关键字后边声明的异常必须是Exception或者Exception的子类

    • 方法内部抛出多个异常对象,那么throws后边也必须声明多个异常;

      方法内部如果抛出了多个异常对象有子父类关系,那么直接声明父类异常即可。

    • 调用了一个声明抛出异常的方法,就必须处理声明的异常。

      要么使用throws声明抛出,交给方法调用者,最终交给JVM

      要么try…catch自己处理异常

2.4捕获异常try…catch

  • 异常处理的第二种方法:自己处理。

    • try{

      ​ 可能产生异常的代码;

      }catch(xxException e){

      ​ 异常的处理逻辑;一般工作中会记录到日志中。

      }catch(){

      …}

    • try中可能抛出多个异常对象,那么可以使用多个catch来处理这些异常对象。

    • 如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕之后,继续执行catch之后的代码

    • 如果try中没有异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try…catch之后的代码。

  • throwable

    • getMessage() 返回此 throwable 的简短描述。
    • toString() 返回此 throwable 的详细消息字符串。
    • printStackTrace() JVM默认此方法,打印异常信息最全面。

2.5 finally代码块

try…catch中,try异常后的代码块不执行,catch后的代码执行。

  • finally中的代码块无论是否出现异常,一定可以执行
    • finally不能单独使用,必须和try一起使用
    • finally一般用于资源释放/回收,无论程序是否出现异常,都要进行资源释放发。

2.6异常注意事项

多个异常使用捕获该如何处理?

  • 多个异常分别处理。每个异常一个try…catch

  • 多个异常一次捕获,多次处理。一个try,多个catch.(如果多个异常有子父类关系,catch中子类异常必须写在前面)

  • 多个异常一次捕获一次处理。 一个try…一个catch(一个Exception全部处理)。

  • 运行时异常可以不处理,默认给虚拟机处理。

如果finally中有return语句,永远返回finally中的结果,应避免该情况。

  • 如果父类抛出多个异常,子类重写父类该方法时,抛出 和父类相同的异常 / 父类异常的子类 / 不抛出异常

  • 父类方法没有抛出异常,子类重写父类方法时不可抛出异常。此时子类产生异常,只捕获,不能声明抛出。

    【父类异常什么样,子类一场就什么样。】

3. 自定义异常

  • 概述

    • java提供的异常类不够用,自己自定义一些异常类。

    • 格式:public class xxxException extends Exception / RuntimeExcetion{
      	添加空参数构造方法;public xxxException(){ }//空参构造
      	添加带构造信息构造方法;public xxxException(String str){ super(str); }//调用父类方法解决异常
      }
      注意:1.自定义异常类一般都是以Exception结尾,说明该类是一个异常类。
      	2.自定义异常类,必须的继承Exception或者RuntimeException	
      		继承exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try...catch
              继承RuntimeException:那么自定义的异常类就是一个运行异常。无需处理,交给虚拟机处理。中断处理。
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
  • 自定义异常练习

    要求:模拟注册操作,如果用户名已经存在,则抛出异常并提示:亲,该用户名已存在

    分析:

    1. 使用数组保存已经注册过用户名(数据库)

    2. 使用scanner获取用户输入的注册的用户名(前端,页面)

    3. 定义一个方法,对用户输入的注册的用户名进行判断

      遍历存储到已经注册过用户名的数组,获取每一个用户名

      使用获取到的用户名和用户输入的用户比较

      ​ true:用户名已存在,抛出RegisterException异常,告知用户“亲,该用户已经被注册”。

      ​ false:继续遍历比较。循环结束,提示 ”恭喜注册成功“。

第八章 多线程

1线程类

  1. 并发与并行

    • 并发:交替执行。单核单线程
    • 并行:同时执行,
  2. 线程与进程

    • 进程:一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;
    • 线程:线程是进程执行中的一个执行单元,负责当前进程中的程序的执行,一个进程至少有一个线程;一个进程可以有多个线程。一个程序运行至少有一个进程,一个进程可以包含多个线程。
    • 线程调度
      • 分时调度:所有线程轮流使用CPU的使用权,平均分配给每个线程占用时间。
      • 抢占调度:优先级高的线程先使用CPU,优先级相同,随机选择。JAVA使用抢占调度。
  3. 创建线程类

    • 主线程:执行主(main)方法的线程。

    • 单线程程序:java程序中只有一个线程。执行main方法开始,从上到下依次执行。

    • 多线程创建方式一:创建Thread类的子类

      • java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类。

      • 实现步骤:

        1.创建一个Thread类
        2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么?)
        3.创建Thread类的子类对象
        4.调用Thread类中的start方法,开启新的线程,执行run方法
        	void start() 使该线程开始执行;java虚拟机调用该线程的run方法
        	结果是两个线程并发的运行;当前线程(从调用返回给start方法)和另一个线程(执行其run方法)。
        	多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7

2多线程

  1. 多线程原理

  2. Thread类:

    • Thread类构造方法:

      • public Thread():分配一个新的线程对象。
      • public Thread(String namr):分配一个指定名字的线程对象。
      • public Thread(Runnable target):分配一个带有指定目标新的线程对象。
      • public Thread(Runnable target, String name):分配一个带有指定目标的线程对象并指定名字。
    • Thread常用方法:

      • public String getName():获取当前线程的名字。

        获取线程的名称:在run方法中

        1. 直接使用Thread类中的方法getName() String getName()返回该线程的名称。
        2. 先获取当前在执行的线程static Thread currentThread(),使用线程的.getName()获取线程的名称。

        设置线程中的名称:

        1. 直接使用Thread类中的方法 void setName(String name)设置线程名称。
        2. 创建一个带参构造方法,参数传递线程名称;调用父类的带参构造方法,把线程名称传递给父类,让父类(Thread)给子线程
      • public void start():此线程开始执行;Java虚拟机调用用此线程的run方法。

      • public void run():此线程要执行的任务在此处定义代码。

      • public static void sleep(long milllis):使当前正在执行的线程暂停毫秒数值。

        可以让主程序暂停执行。“秒表打印”。

      • public static Thread currenThread():返回当前正在执行的线程对象的引用。

  3. 多线程创建方式二:

    • 创建runnable接口的实现类,然后实现方法。
      1. 创建一个Runnable接口的实现类;在其中重写run方法,设置线程任务。
      2. 创建一个Runnable实现类的对象。
      3. 创建一个Thread类对象,构造方法中传递Runnable接口的实现类对象。
      4. 调用Thread中的方法,开启新的线程执行run方法。
  4. Thread和Runnable的区别:

    • 实现Runnable接口创建多线程的好处

      1. 一个类只能继承一个类,不能继承其他类。实现了Runnable接口,还可以继承其他类,实现其他接口。

      2. 在增强程序扩展性,降低程序耦合性(解耦)。

        实现Runnable接口的方式,把设置线程任务和开启新线程进行分离(解耦)。

        实现类中,重写run方法: 就是用来设置线程任务。

        创建Thread类对象,调用start方法:开启新线程。

  5. 匿名内部类:继承/实现;重写;创建子类;一步完成这三个操作。

格式:

new Thread(){
    重写run方法
}.start();	//线程的父类

 Runnable r = new Runnable(){
     重写run方法
 };//实现runnable接口
new Thread(r).start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3线程安全

  1. 线程安全

    多线程访问共享数据会发生安全问题。

    保证线程安全:一个线程在访问共享数据时,其他线程只能等待。保证:始终只有一个线程在卖票。

  2. 线程同步:解决线程安全问题。

    • 有3种方法:同步代码块;同步方法;lock锁机制。
  3. 同步代码块

    synchronize(锁对象){
        访问了共享数据的代码/可能出现安全问题的代码
    }
    注意:通过代码块中的锁对象,可以使用任意的对象。
        必须保证多个线程使用的锁对象相同。
        锁对象作用:吧代码块锁住,只让一个线程在同步代块中执行。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用方法:将代码块写进Runnable实现类的run方法中。

  4. 同步方法

    public synchronized void method(){ 可能会产生安全问题的代码块}

    使用步骤:

    • 把访问了共享数据的方法抽取出来,放到加了sunchronized修饰符的方法中。
    • 在实现类的方法中调用该method方法。

    同步方法也会把方法内部代码锁住,只让一个线程执行。

    同步方法的锁对象就是 this。

    【静态同步方法:】

    1. 在同步方法前和使用的数据前加上static。也能保证线程安全。
    2. 静态方法的锁对象:本类的class属性/class文件对象。
  5. lock机制(用的多)

    • lock锁java.util.current.locks

    • Lock接口种两个重要方法:lockunlock

      1. 在成员位置创建ReentrantLock对象(Lock接口的实现类对象)。
      2. 可能出现安全问题的代码前,调用Lock接口中Lock方法,获取锁。
      3. 在可能出现安全问题的额代码后调用Lock接口中的方法unlck释放锁。

      改进:将安全问题代码写进try中,将l.unlock()写进finally代码块中,无论是否出现异常都会释放锁。

##4线程状态

  1. 线程状态概述

    线程状态导致状态发生的条件
    New(新建)线程刚被创建,但是并未启动。还没调用start方法。
    Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己的代码,也可能没有,取决于操作系统处理器
    Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变为Runnable状态。
    Waiting(无限等待)一个线程等待另一个线程执行一个动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法唤醒。
    Timed Wainting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Wainting状态。这一状态将一直保持到超时期满或者收到唤醒通知。带有超时参数的常用方法有Thread.sleep,Object.wait。
    Teminated(被终止)run方法正常运行结束而消亡;或者因为没有捕获的异常终止了方法而死亡。
    • 等待唤醒案例: 线程之间的通信。

      1. 创建第一个线程,调用wait方法 ,进入wainting(无限等待)状态。

      2. 创建第二个线程,执行结束后调用notify方法,唤醒第一个线程执行。

        • 两个线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。
        • 两个进程必须使用同一个锁对象。
        • 只有锁对象才能调用wait和notify方法
      3. void wait():其他线程调用notify或notifyAll方法前,导致当前线程等待。

        void notify():唤醒在此监视器上的等待的单个线程;会继续执行wait方法之后的代码。

  2. Timed Waiting(计时等待)

    • 使用sleep(long m)方法后,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态。
    • 使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,进入到Runnable/Blocked状态。
    • notifyAll():如果有多个等待线程,唤醒所有的等待线程。
  3. BLOCKED(锁阻塞)

  4. Waiting(无限等待)

第九章 等待与唤醒机制

1 等待唤醒机制

  1. 线程间通信

  2. 等待唤醒机制

    wait():

    notify():唤醒等待时间最久的线程。

    notifyAll():唤醒等待线上所有的等待的线程。

    唤醒之后也不是立即就可以执行,进入Blocked状态的话还是需要争夺CPU使用权;进入Runnable状态的话就可以执行了。

  3. 调用wait与notify方法注意的细节。

    • wait方法和Notify方法必须要由同一个锁对象调用。因为:同一个锁通过notify唤醒同一个锁对象调用的wait方法。
    • wait方法和notify方法是属于Object方法的。因为:锁对象任意,任意对象所属类都继承了Object类。
    • wait方法和notify方法必须要在同步代码块或者同步函数中使用。因为:必须通过锁对象调用这两个方法。

1.3生产者与消费者问题

A:生产者线程类(包子铺)。

  • 是一个线程类,可以继承Thread。线程任务:生产包子
  • 对包子状态判断:有包子等待;无包子生产(交替生产两种不同的包子)
  • 生产包子以后修改包子状态,唤醒吃货线程。
  • 成员位置定义包子变量,带参构造为包子变量赋值。

B:消费者线程类(吃货):线程类。

  • A和B通信。
  • A和B必须同时同步技术保证两个线程只有一个在执行。
  • 锁对象必须保证唯一,可以使用包子对象作为锁对象。
  • 包子铺类和吃货的类需要把包子作为参数传递进来。

C:包子类:皮,馅,状态。

D:测试线程:创建包子对象;创建包子铺(包子)对象.start;创建吃货(包子)对象.start。

2线程池

2.1线程池概念

线程池:容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

使用线程池的好处:

  1. 降低资源消耗。
  2. 提高响应速度。
  3. 提高线程的可管理性。

2.2线程池的使用

java.util.concurrent.Executors:生产线程池的工厂类。(jdk1.5之后提供的)

static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池。

返回值:

  • ExecutorService接口,返回的是ExecutorSercive接口的实现类对象,可以使用ExecutorSercive接口接收。

java.util.concurrent.ExecutorService:线程池接口

  • submit(Runnable task):从线程池中获取线程,调用start方法,实行线程任务。提交一个Runnable任务用于执行。
  • void shutdown():关闭/销毁线程池。

线程池持使用步骤:

  1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个之当线程数量的线程池。
  2. 创建一个类,实现Runnabl接口,重写run方法,设置线程任务。
  3. 调用ExcutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法。
  4. 调用ExecutorService中的方法shutdown销毁线程池(不建议执行,销毁了就少了一个线程了。)

3Lambda表达式

3.1函数式编程思想概述

  • 面向对象的思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成任务。强调通过对象的形式来做。

    函数时编程思想:强调做什么,而不是以什么形式做。只要能获取到结果就行,重视结果,不重视过程。

  • 冗余的Runnable代码

  • 编程思想转换:体验Lambda

  • new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"创建了一个新线程");
            }).start();
    
    • 1
    • 2
    • 3

3.4体验Lambda的更优化写法

3.5匿名内部类回顾

3.6Lamda标准格式

  • 一些参数

  • 一个箭头

  • 一段代码

  • 标准格式:

    (参数类型  参数名称) -> { 代码语句/重写方法的代码 }
    ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔。
     ->:把参数传递给方法体{}{}:重写接口的抽象方法。
    
    • 1
    • 2
    • 3
    • 4
    1. Lambda的参数和返回值

    2. Lambda标准模式(有参数有返回)

    3. 练习:使用Lambda省略格式

      可推导可省略。

      可省略的内容跟:

      1. (参数列表):括号中参数列表的数据类型,可以省略不写。
      2. (参数列表):括号中的参数如果只有一个,那么类型和括号都可以省略。
      3. {一些代码}:如果大括号中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)。要省略,这三个要一起省略。
    4. Lambda使用前提:

      • 使用Lambda必须具有接口,且要求接口中有且只有一个抽象方法。
      • 使用Lambda必须具有上下文推断。方法的参数或者局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

      【备注:】有且仅有一个抽象方法的接口,称为“函数式接口”。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/252841
推荐阅读
相关标签
  

闽ICP备14008679号