java异常和多线程

Java异常

       Java异常时java提供的一种识别及响应错误的一执行机制

Java异常机制可以是程序中的异常处理代码和正常业务代码分离

Java异常机制用的关键字

       Try catch finally throw throws

• try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。

• catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。

• finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。

• throw – 用于抛出异常。

• throws – 用在方法签名中,用于声明该方法可能抛出的异常

(3)了解throws和throw的基本用法

throws是用于声明抛出的异常,而throw是用于抛出异常。

  MyException是继承于Exception的子类。test()的try语句块中产生ArithmeticException异常(除数为0),并在catch中捕获该异常;接着抛出MyException异常。main()方法对test()中抛出的MyException进行捕获处理。

Objects非空判断

 

* `public static <T> T requireNonNull(T obj)`:查看指定引用对象不是null。

**声明异常**:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。

 

关键字**throws**运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).

 

**声明异常格式:**

 

~~~

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{   }   

~~~      

Throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开

捕获异常try..catch 如果程序抛出异常我们会利己处理异常

try{

     编写可能会出现异常的代码

}catch(异常类型  e){

     处理异常的代码

     //记录日志/打印异常信息/继续抛出异常

}

**finally**:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。

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

 

  1. 多个异常分别处理。

  2. 多个异常一次捕获,多次处理。

  3. 多个异常一次捕获一次处理。

 

  一般我们是使用一次捕获多次处理方式,格式如下:

 

  ```java

  try{

       编写可能会出现异常的代码

  }catch(异常类型A  e){  当try中出现A类型异常,就用该catch来捕获.

       处理异常的代码

       //记录日志/打印异常信息/继续抛出异常

  }catch(异常类型B  e){  当try中出现B类型异常,就用该catch来捕获.

       处理异常的代码

       //记录日志/打印异常信息/继续抛出异常

  }

  ```

 

  > 注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。

 

* 运行时异常被抛出可以不处理。即不捕获也不声明抛出。

 

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

 

* 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。

 

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

我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义好的,此时我们根据自己业务的异常情况来定义异常类。例如年龄负数问题,考试成绩负数问题等等。

 

在上述代码中,发现这些异常都是JDK内部定义好的,但是实际开发中也会出现很多异常,这些异常很可能在JDK中没有定义过,例如年龄负数问题,考试成绩负数问题.那么能不能自己定义异常呢?

 

**什么是自定义异常类:**

 

在开发中根据自己业务的异常情况来定义异常类.

 

自定义一个业务逻辑异常: **RegisterException**。一个注册异常类。

 

**异常类如何定义:**

 

1. 自定义一个编译期异常: 自定义类 并继承于`java.lang.Exception`。

2. 自定义一个运行时期的异常类:自定义类 并继承于`java.lang.RuntimeException`。

多线程

       并行和并发

       并发值两个或多个事件在同一时间内发生

       并行指两个或多个事件在同一时刻发生

4.3 创建线程类

 

Java使用`java.lang.Thread`类代表**线程**,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来**创建**并**启动多线程**的步骤如下:

 

1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。

2. 创建Thread子类的实例,即创建了线程对象

3. 调用线程对象的start()方法来启动该线程

构造方法

Thread()分配一个新的线程对象

Thread(string name)分配一个指定名称的新的线程对象

Thread(runnable target)分配一个带有指定目标的新的线程对象

Thread(runnable target,string name)分配一个带有指定目标的新的线程对象并指定名字

常用方法

getName()获取当前线程名称

start()导致该线程开始执行,java虚拟机调用该线程的run方法

run()此线程要执行的任务在此处定义代码

sleep(long millis)是当前正在执行的线程以毫秒数暂停

currentThread()返回正在执行的线程对象的引用

第二种创建线程的方式实现runnable接口

步骤:

       定义runnable接口的实现类,并重写该方法的run()方法run()方法同样是线程要执行的代码体

床架runnable实现类的实例,并将此实例作为作为thread的target来创建thread对象,该thread才是真正的线程对象

Start()执行

Thread和runnable的区别

如果一个类继承thread,则不适合资源共享,如果实现runnable接口,则很容易实现资源共享

实现runnable接口比继承thread类所具有的优势

  1. 适合多个相同的程序代码的线程共享一个资源
  2. 避免java单继承的局限性增加程序的奖状性
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立
  4. 线程池只能实现runable或callable类线程,不能直接放入继承的Thread类

匿名内部类实现线程的创建

线程安全

       如果多个线程同时运行,并且代码是同一个程序每次运行结果和单个的结果是一样的,而其他变量的值也和预期是一样的,这就是线程安全

当使用多个线程访问同一资源时,且多个线程对资源有写的操作是就容易出现线程安全问题,java提供了同步机制synchronized来解决

  1. 同步代码块
    1. Synchronized(同步锁){
      1. 需要进行同步操作的代码
    2. }

1锁对象可以是任意类型

多个线程对象使用同一把锁

  1. 同步方法
    1. 使用synchronized修饰的方法就叫同步方法,在线程执行该方法的时候其他线程只能在外等待
  2. lock锁
    1. public void lock()加同步锁
    2. public void unlock()释放同步锁

线程状态

New()新建

Runnable()可运行

Blocked()锁住塞

Waiting()无线等待

Timed waiting()计时等待

Teminated()被终止

Timed waiting()计时等待

常用的是调用sleep()方法,

Blocked()锁住塞

根据同步机制,当线程A和线程B采用的是同一个锁南无如果线程A获取到锁静茹runnable状态,那么线程B就会进入所阻塞状态

Waiting()无线等待

一个线程调用了摸个对象的object.wait()方法就会等待另一个线程调用此对象的objectnotify()方法,或者object.notifyAll()方法体现为多个线程的相互通信

 

就是在一个线程进行了规定操作后,就进入等待状态(**wait()**), 等待其他线程执行完他们的指定代码过后 再将其唤醒(**notify()**);在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。

 

wait/notify 就是线程间的一种协作机制。

线程池

       一个可以容纳多个线程的容器其中的线程可以反复使用,省去了创建线程对象的操作

1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

使用线程池中线程对象的步骤:

 

1. 创建线程池对象。

2. 创建Runnable接口子类对象。(task)

3. 提交Runnable接口子类对象。(take task)

4. 关闭线程池(一般不做)

 

Runnable实现类代码:

 

~~~java

public class MyRunnable implements Runnable {

    @Override

    public void run() {

        System.out.println("我要一个教练");

        try {

            Thread.sleep(2000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("教练来了: " + Thread.currentThread().getName());

        System.out.println("教我游泳,交完后,教练回到了游泳池");

    }

}

~~~

 

线程池测试类:

 

~~~java

public class ThreadPoolDemo {

    public static void main(String[] args) {

        // 创建线程池对象

        ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象

        // 创建Runnable实例对象

        MyRunnable r = new MyRunnable();

 

        //自己创建线程对象的方式

        // Thread t = new Thread(r);

        // t.start(); ---> 调用MyRunnable中的run()

 

        // 从线程池中获取线程对象,然后调用MyRunnable中的run()

        service.submit(r);

        // 再获取个线程对象,调用MyRunnable中的run()

        service.submit(r);

        service.submit(r);

        // 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。

        // 将使用完的线程又归还到了线程池中

        // 关闭线程池

        //service.shutdown();

    }

}

~~~

 

Lambda表达式

       函数式编程思想

这个`RunnableImpl`类只是为了实现`Runnable`接口而存在的,而且仅被使用了唯一一次,所以使用匿名内部类的语法即可省去该类的单独定义,即匿名内部类:

 

```java

public class Demo04ThreadNameless {

       public static void main(String[] args) {

              new Thread(new Runnable() {

                     @Override

                     public void run() {

                            System.out.println("多线程任务执行!");

                     }

              }).start();

       }

}

```

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

 

1. 使用Lambda必须具有接口,且要求**接口中有且仅有一个抽象方法**。

   无论是JDK内置的`Runnable`、`Comparator`接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。

2. 使用Lambda必须具有**上下文推断**。

   也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

 

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值