01-异常、多线程

1、异常

1.1、异常概述

1.1.1、异常介绍和体系

  • 异常就是程序可能出现了不正常的情况。程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止
  • demo
  • Error
    • 严重问题,通过代码无法处理。比如:电源断了
  • Exception
    • 称为异常,它表示程序本身可以处理的问题

1.1.2、异常体系

  • Exception编译时异常就是在编译的时候出现的异常
  • RuntimeException运行时异常,就是在运行的时候出现的异常
  • 虚拟机默认处理异常的方式
    • 打印异常信息,终止程序

1.2、异常的处理方式

1.2.1、throws抛出异常

  • throws格式
    • 修饰符 返回值类型 方法名(参数) throws 异常类1,异常类2{}
  • throws的作用
    • 抛出异常,把异常抛给调用者处理

1.2.2、try...catch捕获异常

  • try...catch格式

    • try {

      ​ 可能出现异常的代码

      }catch(异常类名 变量名) {

      ​ 异常的处理代码;

      }

  • 好处

    • 可以让程序继续往下执行

1.2.3、finally代码块

  • 格式

    • try {

      ​ 可能出现异常的代码

      }catch(异常类名 变量名) {

      ​ 异常的处理代码;

      }finally {

      ​ 代码;(该代码一定会被执行)

      }

1.2.4、Throwable的成员方法

  • public String getMessage()返回此throwable的详细消息字符串
  • public String toString()返回此可抛出的简短描述
  • public void printStackTrace() 把异常的错误信息输出在控制台

1.2.5、注意事项

  • 子类重写方法throws的异常要比父类方法throws的异常相同或更少
  • 父类方法没有throws异常,子类方法只能try...catch处理
  • PS:
    • 这里的异常指的是编译异常

1.3、自定义异常

  • 概念
    • 就是为了让控制台的报错信息更加的见名知意

2、线程

2.1、多线程的好处

  • 让程序可以"同时"做多件事情

2.2、并发和并行

  • 并发
    • 多个事件在同一时刻,同时执行
  • 并行
    • 多个事件在同一时刻,交替执行

2.3、进程和线程

  • 进程概念
    • 是一个正在运行的程序
  • 线程概念
    • 线程就是来执行代码的
  • PS:线程是进程的执行单元

2.4、多线程运行原理

  • CPU在多个线程间快速切换,造成"同时"运行的假象
    • PS:本质上是交替执行,只是交替的很快,让用户感觉是同时执行

2.5、多线程的实现方式

2.5.1、继承Thread类

  • 概述

    • Thread类表示线程,通过Thread类启动多个线程
  • 格式

    • // 1.定义类继承Thread类
          public class MyThread extends Thread {
              // 2.重写run方法, 编写新线程要执行的代码
              @Override
              public void run() {
                  for (int i = 0; i < 20; i++) {
                      System.out.println("run: " + i);
                  }
              }
          }

      // 3.创建子类对象
          MyThread mt = new MyThread();

      // 4.调用start()方法
          mt.start(); // 启动新线程,新的线程会自动执行mt.run()方法

  • Thread类启动线程步骤

      1. 定义类继承Thread类
      1. 重写run方法,编写新线程要执行的代码
      1. 创建子类对象
      1. 调用star()方法
  • 注意

    • 多线程程序随机并发执行
  • 优缺点

    • 优点
      • 编码简单
    • 缺点
      • 线程类已经继承Thread,无法继承其他类,不利于扩展

2.5.2、run()方法和start()方法的区别

  • 为什么要重写run()方法
    • 因为run()方法是用来封装被线程执行的代码
  • run()方法和start()方法的区别
    • run方法是继承线程封装执行的代码,直接调用,相当于普通方法的调用,并没有开启线程
    • start方法是启动新线程,新线程自动调用run()方法
  • 为什么要先启动子线程,再执行主线程任务
    • 避免主线程任务提前执行完毕了

2.5.3、实现Runnable接口

  • Thread构造器

    • public Thread(Runnable target)(其中之一)
      • 根据Runnable对象创建线程对象
  • 格式

    • MyRunnble r = new MyRunnble();
              Thread t = new Thread(r);
              t.start();
              for (int i = 0; i < 20; i++) {
                  System.out.println("main" + i);
              }
  • 实现Runnable接口启动线程步骤

      1. 定义一个类MyRunnable实现Runnable接口
      1. 在MyRunnable类中重写run()方法
      1. 创建MyRunnable类的对象
      1. 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
      1. 调用start()方法启动线程
  • 优缺点

    • 优点:可以继续继承类和实现接口,扩展性强
    • 缺点:代码复杂一点,需要创建两个对象

2.5.4、匿名内部类创建线程

  • Demo

    • public class Demo06 {
      public static void main(String[] args) {
      new Thread(new Runnable() {
      @Override
      public void run() {
      while (true) {
      System.out.println("我是新线程!");
      }
      }
      }).start();
      while (true) {
      System.out.println("我是主线程!");
      }
      }
      }

3、线程类的常见方法

3.1、获取和设置线程名称

  • 方法

    • String getName(): 获取线程的名字
      • void setName(String name):设置线程的名字
  • 构造器

    • Thread(String name) : 创建线程对象时,设置线程的名称
    • Thread(Runnable r, String name) : 创建线程对象的时候,设置线程的名称

3.2、获得当前线程的对象

  • 方法
    • public static Thread currentThread():返回执行当前代码的线程对象
  • 注意事项
    • 此方法是Thread类的静态方法,可以直接使用Thread类调用
    • 这个方法是被哪个线程执行的,就会得到哪个线程的对象

3.3、线程休眠

  • 方法
    • public static void sleep(long time):让线程休眠指定的事件,单位为毫秒
    • 格式
      • Thread.sleep(1000); // 休眠一秒

4、线程的安全问题

4.1、概念

  • 多个线程同时操作共享资源导致数据错乱,称为线程安全问题

4.2、原因

  • 多线程并发
  • 同时访问共享资源
  • 存在修改共享资源

4.3、多线程取钱业务案例

package Day08._02多线程.demo09多线程卖票;
class SaleTicket00 extends Thread{
private static int TicketCount = 100;
@Override
public void run() {
while (true) {
if (TicketCount > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
TicketCount --;
System.out.println(getName() + "正在买票.........,剩余" + TicketCount + "张票");
}
else {
// System.out.println("票已经卖完了!");
break;
}
}
}
}
public class Demo09 {
public static void main(String[] args) {
SaleTicket00 buy1 = new SaleTicket00();
buy1.setName("buy1");
buy1.start();
SaleTicket00 buy2 = new SaleTicket00();
buy2.setName("buy2");
buy2.start();
SaleTicket00 buy3 = new SaleTicket00();
buy3.setName("buy3");
buy3.start();
}
}

4.4、线程同步思想

  • 加锁
    • 把共享资源进行上锁,每次只能一个线程进入访问,访问完毕以后解锁,然后其他线程才能进来

4.5、线程同步的实现方式

4.5.1、同步代码块

4.5.1.1、格式
  • synchronized (锁对象)
4.5.1.2、作用
  • 给代码块中的代码加锁,解决线程安全问题。
4.5.1.3、原理
  • 每次只能一个线程获取锁进入,执行完毕以后自动解锁,其他线程才可以进来执行
4.5.1.4、要求
  • 原则上:锁对象必须是同一个对象。
  • 语法上:任意对象都可以作为锁,建议使用共享资源作为锁对象。
  • 对于实例方法建 议使用this作为锁对象,此时this应该代表共享资源对象。
  • 对于静态方法建议使用字节码类名.class对象作为锁对象。

4.5.2、同步方法

4.5.2.1、格式
  • public synchronized void 方法名()
4.5.2.2、作用
  • 给方法加锁,解决线程安全问题。
4.5.2.3、同步方法原理
  • 每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
4.5.2.4、同步方法底层原理
  • 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法。
  • 如果方法是实例方法:同步方法默认用this作为的锁对象。
  • 如果方法是静态方法:同步方法默认用类名.class作为的锁对象。

4.5.3、Lock锁

4.5.3.1、API
4.5.3.2、格式
在成员变量位置: private final Lock lock = new ReentrantLock();
    try {
        lock.lock()
        操作共享资源的代码
    } finally {
        lock.unlock();
    }
posted @   OnlyOnYourself-Lzw  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示