Exception类(异常)- Thread类 (线程、多线程)- Timer类(定时器)

发布时间:2023年12月23日

目录

异常

线程

Timer类(定时器)


异常

  1. 概念:Java中程序的一种错误

  2. Java中异常机制:表示程序的某个错误,当错误发生的时候,将程序结束,提示在那个位置出现什么错误

  3. Java中异常的体系结构:每种错误就是一个类

  4. Throwable类:Java中的错误的父类

    1)、Error:错误,不能用Java代码来处理错误

    VirtualMachineError:虚拟机损坏的错误

StackOverflowError:堆栈溢出

2)、Exception:异常,需要使用Java代码来处理错误,异常又分为以下两种情况

  • 编译时异常:编译之前必须处理的异常,强制处理的异常

ParseException:转换日期格式的异常

  • 运行时异常:不强制处理的异常

  1. NullPointerException:空指针异常(对象在堆里面还没有引用的时候,就直接调用方法) 注意:对象在调用方法或属性的时候需要做非空判断

  2. StringIndexOutOfBoundsException:字符串下标越界的异常

    注意:只要操作数组下标,必须要做合法性判断

  3. ArrayIndexOutOfBoundsException:数组下标越界

    注意:操作数组先做非空和空数组的判断

  4. ArithmeticException:算术异常

  5. NumberFormatException:数值的格式异常

  6. ClassCastException:类型转化异常,对象向下转型的时候

  7. IllegalArgumentException:非法参数的异常

  1. 区分编译时异常跟运行时异常

    • 只要异常的父类是RuntimeException就是运行时异常,其他就是编译时异常

  2. 异常处理方式:

    1)、甩锅式 直接在方法上抛出异常或者在方法里面直接抛出

  • throws是在方法的声明上面抛出异常 如:public static void main(String[] args) throws ParseException

  • 在方法体内抛出异常,throw + 异常对象 (特点:可以自己设置条件判断,不满足则抛出异常) 等于是可以用来自定义异常

    区别:如果是在方法声明上加s,方法体内不加s

    提示:只要程序出现抛出的异常,JVM会创建相应对象,再去调用相应的方法来提示错误

2)、背锅式

  • 语法:

    try{

    可能会出现的代码 例如:new SimpleDateFormat("").parse(""); // 这就是一个异常

    }catch(异常类名 参数名) {

    报这种异常错误信息的代码 例如:throw new RuntimeException(e);

    注意:catch的顺序应该是先子后父

    }finally {

    处理异常的出口 作用:关闭资源 特点:无论如何都会执行它,除非前面写了System.exit(0); //终止程序

    }

    public class ExceptionDemo3 {
     ? ?// 声明一个方法
     ? ?public static void m() throws ParseException { ?// 抛出异常,异常抛给调用者,thorws
     ? ? ? ?// 省略方法体
     ? ? ? ?new SimpleDateFormat("yyyy-MM-dd").parse("2023/12/19");
     ?  }
     public static void main(String[] args) 方法1:throws ParseException { // 直接抛给JVM ?
     ? ? ? ? m(); ?// 调用方法就需要再次抛出异常
     ? ? 方法2:
     ? ? ? ? try { ?// 主动捕获
     ? ? ? ? ? ?new SimpleDateFormat("").parse("");
     ? ? ? ? }catch (ParseException e) {
     ? ? ? ? ? ?throw new RuntimeException(e); // 抛出异常
     ? ? ? ? }
     public static void mmm(int i) {
     ? ? ? ?int length = String.valueOf(i).length(); // 将输入的数字转换成字符串,再求字符串长度
     ? ? ? ?if(length != 3) { // 如果长度不等于3则抛出以下异常
     ? ? ? ? ? ?throw new IllegalArgumentException("参数的位数不是一个三位数"); // IllegalArgumentException:非法参数的异常
     ? ? ?  }
     ?  }

  1. 自定义异常:

    1. 作用:程序执行过程有不满条件情况都是异常,需要自己定义

    2. 语法:

      1. 编译时异常:class 自定义的异常名字(Exception结尾) extends Exception {
       ? ?// 自定义编译时异常 
       ? ?书写两个构造方法
       ? ?1. 无参
       ? ?2. 一个有1个字符串参数的构造方法
      }
      2. 运行时异常:class 自定义异常的名字(Exception结尾) extends RuntimeException {
       ? ?// 自定义运行时异常
       ? ?书写两个构造方法
       ? ?1. 无参
       ? ?2. 一个有1个字符串参数的构造方法
      }

线程

  1. 进程:一个应用软件就是一个进程,一大段程序(代码)每一个进程进行运行都有分配独立的内存空间

    • 腾讯视频:进程A

    • 爱奇艺:进程B

    • 优酷:进程C

      注意点:每个进程之间运行相互不干扰

  2. 线程:一个进程里面独立某个功能就是一个线程,一小段代码 (其实软件运行的就是线程)

    • 一个进程里面至少有一个线程,又可以有多个线程

    • 线程的运行也是独立的,每个线程也有独立的运行内存,这个内存是属于进程的内存

  3. 线程执行流程:

    1)、启动线程

    2)、线程进入待运行状态

    3)、cpu会统计目前内存中有多少个待运行的线程

    4)、将运行时间平均分配给每个待运行的线程

    5)、哪个线程获得这个时间片,哪个线程就开始执行,直到时间片用完,线程就暂停

    注意点:在cpu里面同一时间只能运行一个线程

  4. 为什么软件要设计多线程?

    1)、提高性能

    2)、提高响应性

    3)、资源共享

    4)、简化复杂性

    5)、任务分解

    注意点:多线程编程也引入了一些挑战,如竞态条件(Race Conditions)、死锁(Deadlocks)、数据共享与同步等问题,因此,在设计和实现多线程应用程序时,需要小心谨慎,使用适当的同步机制来确保线程安全性。

  5. Java实现多线程的方式:

    1)、继承Thread类,重写run( )方法

    public class Play extends Thread{ ? // Thread的子类
     ? ? ? ?@Override ?// 重写run()方法:线程会获得时间片结束后执行的代码
     ? ?public void run() {
            System.out.println("播放") ? 
     ?  }
    }
    ?
    public class PlayTest { ?// 测试类
     ? ?public static void main(String[] args) {
     ? ? ? ?// 1. 创建线程
     ? ? ? ?Play p1 = new Play(); // 开启一个线程 Thread-0
     ? ? ? ?Play p2 = new Play(); // 开启一个线程 Thread-1
     ? ? ? ?Play p3 = new Play(); // 开启一个线程 Thread-2 ? ?
     ? ? ? ?// 现在就是多线程
     ? ? ? ?// 2. 开启线程,调用Thread父类的 start()的方法
     ? ? ? ?p1.start(); // 启动p1线程 ? ?
     ? ? ? ?p2.start(); // 启动p2线程
     ? ? ? ?p3.start(); // 启动p3线程
     ?  }
    }

    2)、实现Runnable接口,重写run( )方法

    public class Down implements Runnable{
     ? ?@Override // 重写run()方法:线程会获得时间片结束后执行的代码
     ? ?public void run() {
     ? ? ? ?for (int i = 1; i <= 10 ; i++) {
     ? ? ? ? ? ?System.out.println("下载视频第" + i + "次");
     ? ? ?  }
    }
    ?
    public class DownTest { // 测试类
     ? ?// 1、创建Runnable对象  由于Runnable接口不能够直接new,所以需要利用他的实现类来间接操作它,后面的Thread(d1)会将它向上转型
     ? ? ? ?Down d1 = new Down();
     ? ?// 2、创建线程对象
     ? ? ? ?Thread t1 = new Thread(d1); // 创建第一个线程  // 这里使用了Thread(Runnable target) 根据传入Runnable对象创建线程,向上转型
     ? ? ? ?Thread t2 = new Thread(d1); // 创建第二个线程
     ? ? ? ?Thread t3 = new Thread(d1); // 创建第三个线程
     ? ?// 3、启动线程
     ? ? ? ?t1.start();
     ? ? ? ?t2.start();
     ? ? ? ?t3.start();
    }
  6. Thread类中常用的方法:

    1)、常用字段(常量)

    • MAX_PRIORITY:线程可以拥有的最大优先级。

    • NORM_PRIORITY:分配给线程的默认优先级。

    • MIN_PRIORITY:线程可以拥有的最小优先级。

      注意:1. 线程的优先级:1-10 2. 优先级越高获得时间片的概率越大 3. 线程默认优先级都是:5

    System.out.println("最高优先级:"+ Thread.MAX_PRIORITY); // 打印10 
    System.out.println("默认优先级:"+ Thread.NORM_PRIORITY); // 打印5
    System.out.println("最低优先级:"+ Thread.MIN_PRIORITY); // 打印1

    2)、构造方法

    • Thread( ):创建默认名字的线程。

    • Thread(String name):创建一个指定名字的线程 // 后面用setName也可以修改

    • Thread(Runnable target) :根据传入Runnable对象创建线程,线程的名字是默认的

    • Thread(Runnable target, String name) :根据传入Runnable对象创建线程,线程的名字是自己来指定的

    3)、常用方法

    • getName( ):获取线程名字

    • setName(String name) :设置线程名字 如果子类继承了thread,则可以直接使用方法名调用 这是一个非静态方法,jvm会自动生成一个this来表示子类对象

    • setPriority(int newPriority) :设置线程优先级

    • getPriority( ) :获取线程的优先级

    • setDaemon(boolean on):设置线程为守护线程 // 线程分为:前台线程,后台线程(守护线程)

    • isDaemon():判断线程是否位守护线程 // GC:垃圾回收机制 就是一个守护线程

    • setDaemon(boolean on) :将此线程标记为守护线程或用户线程。

    • sleep(long millis):设置线程休眠的时间

    • currentThread() :获取当前线程 // 类方法

  7. 线程的状态

    1)、新建 new Play( );

    2)、就绪 new Play( ).start( );

    3)、运行 执行run方法的时候

    4)、阻塞 sleep( )

    5)、死亡(结束) run方法执行完毕

  8. 多线程的安全

  • 线程的执行是靠cpu分配时间片

  • 以下案例存在一票多卖:多个线程同时访问一个数据,同一个数据被访问多次,存在线程安全问题

  • 线程安全问题是怎样发生:线程要执行的代码还没有全部执行完,另一个线程又开始执行

    1)、处理解决这个线程安全问题:就是让线程排队? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??方法1:同步代码块

  • 注意: 一个线程访问一个对象中的synchronized(获取锁的对象)同步代码块时,其它线程试图访问该对象的线程将被阻塞

    synchronized (参数:引用类型,所有线程能共享的(static修饰的或者字节码对象)) { // 写在run方法里面的

    这个位置是你写的代码

    }

/*
*  1.使用继承Thread类的方式来写这个卖票案例
*/
public class Tickets extends Thread{ ?// 继承Thread类,继承是单继承
 ? ?private static int count = 1; // 这里因为是继承关系,后面测试为了实现多线程就需要多创建对象,这样每个对象就都会有一个独立的count,所以必须要用static修饰,为了让每个对象共享一个count,防止一票多卖 ? 结合后面测试类再合起来理解
?
 ? ?public Tickets()  { ?// 无参构造
 ?  }
?
 ? ?public Tickets(String name) { ?// 有参构造
 ? ? ? ?super(name); // 调用父类的有参构造
 ?  }
 ? ?
public void run() { ?// 重写run方法
 ? ? ? ?for (int i = 0; i < 30; i++) { // 循环控制执行次数,共卖30次
 ? ? ? ? ? ?synchronized (this.getClass()) { ?// 参数是获取对象的字节码对象,这样足以保证所有的对象都是共享一把锁,"获取锁的地方是唯一的" 这玩意儿不能写在循环外面了,否则会导致不同对象卖同一张票,原因是写在外面,当count=1的时候,有可能其他的线程已经进入了循环里面
 ? ? ? ? ? ? ? ?if(count <= 30) {
 ? ? ? ? ? ? ? ? ? ?// 获取当前线程的名字
 ? ? ? ? ? ? ? ? ? ?String name = Thread.currentThread().getName();
 ? ? ? ? ? ? ? ? ? ?System.out.println(name + "窗口:卖出第" + count + "张票");
 ? ? ? ? ? ? ? ? ? ?count++;
 ? ? ? ? ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ? ? ? ? ?Thread.sleep(100); // 设置线程休眠时间
 ? ? ? ? ? ? ? ? ?  } catch (InterruptedException e) { // 线程中断异常  系统强制抛出
 ? ? ? ? ? ? ? ? ? ? ? ?throw new RuntimeException(e); 
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
}
// 测试类
public class TicketsTest {
 ? ?public static void main(String[] args) {
 ? ? ? ?// 这里创建了3个对象,如果上面的count不用static修饰,那么每个实例对象,都会有一个count,那就会造成一票多卖的情况
 ? ? ? ?Tickets t1 = new Tickets("泷泽"); 
 ? ? ? ?Tickets t2 = new Tickets("深田");
 ? ? ? ?Tickets t3 = new Tickets("桃子");
 ? ? ? ?// 开启线程
 ? ? ? ?t1.start();
 ? ? ? ?t2.start();
 ? ? ? ?t3.start();
 ?  }
}
?
?
/*
* 2. 使用实现接口的方式来完成卖票的案例
*/
public class Tickets implements Runnable { // 实现Runable接口
 ? ?private int count = 1; // 这里可以是实例变量,也可以是类变量,原因是后面只需要创建一个对象,他们共享这一个对象
 ? ?
 ? ?public void run() { ?// 重写run方法
 ? ? ? ?for (int i = 0; i < 30; i++) { // 循环控制执行次数,共卖30次
 ? ? ? ? ? ?synchronized (this.getClass()) { 
 ? ? ? ? ? ? ? ?if(count <= 30) {
 ? ? ? ? ? ? ? ? ? ?// 获取当前线程的名字
 ? ? ? ? ? ? ? ? ? ?String name = Thread.currentThread().getName();
 ? ? ? ? ? ? ? ? ? ?System.out.println(name + "窗口:卖出第" + count + "张票");
 ? ? ? ? ? ? ? ? ? ?count++;
 ? ? ? ? ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ? ? ? ? ?Thread.sleep(100); // 设置线程休眠时间
 ? ? ? ? ? ? ? ? ?  } catch (InterruptedException e) { // 线程中断异常  系统强制抛出
 ? ? ? ? ? ? ? ? ? ? ? ?throw new RuntimeException(e); 
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
}
// 测试类
public class TicketsTest {
 ? ?public static void main(String[] args) {
 ? ? ? ?// 1、创建Runnable对象
 ? ? ? ?Tickets tickets = new Tickets();
 ? ? ? ?// 2、创建线程
 ? ? ? ?Thread t1 = new Thread(tickets, "泷泽"); ?// 将tickets对象传到thread的参数中,这里tickets对象的类型是Runable,所以会发生向上转型
 ? ? ? ?Thread t2 = new Thread(tickets, "有菜"); ?// 因为tickets是Runable的实现类,相当于是利用了多态的特征
 ? ? ? ?Thread t3 = new Thread(tickets, "一香"); ?// 如此一来,他们几个对象都属于了一个对象
 ? ? ? ?// 3、启动线程
 ? ? ? ?t1.start();
 ? ? ? ?t2.start();
 ? ? ? ?t3.start();
 ?  }
}

? ? ? ? ?方法2:同步方法

/*
*  1.使用继承Thread类 ? 2.使用实现接口的方式
*/
 ? ?// 使用同步方法  也就是将synchronized单独写在一个方法里,以下这个就是同步方法
public class Tickets implements Runnable {
 ? ?private int count = 1; // 可以是实例变量,也可以是类变量
 ? ?
 ? ?public static synchronized void sale() { ?// 同步实例方法的锁默认是this,同步类方法的锁是当前类的字节码对象
 ? ? ? ?if (count <= 30) {
 ? ? ? ? ? ?String name = Thread.currentThread().getName(); // 获取当前线程的名字
 ? ? ? ? ? ?System.out.println(name + "窗口:卖出第" + count + "张票");
 ? ? ? ? ? ?count++;
 ? ? ? }
 ?  }
 ? ?
 ? ?@Override
 ? ?public void run() { ? // 重写run方法
 ? ? ? ?for (int i = 0; i < 30; i++) {
 ? ? ? ? ? ?sale(); // 调用同步方法
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?Thread.sleep(100);
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?throw new RuntimeException(e);
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
}

? ? ? ? 方法3:同步锁

/* 同步锁:ReentrantLock,lock():上锁,unlock():解锁
* ?1.使用继承Thread类 ? 2.使用实现接口的方式 都ok
*/

?// 使用同步锁
public class Tickets3 extends Thread{
? ? // 创建同步锁
? ? private ?static ReentrantLock lock = new ReentrantLock(true); // 默认不带公平机制,加上true就带有公平机制,也就是大概率所以对象抢票的几率相同 ? 这里由于是继承关系,后面测试的时候需要创建多个对象,所以为了防止锁不住,所以需要加static,private是为了防止外部类访问
? ??
? ? private static int count = 1;
? ? public Tickets3() {
? ? }
? ??
? ? public Tickets3(String name) {
? ? ? ? super(name);
? ? }
? ??
? ? @Override
? ? public void run() { ?// 重写run方法
? ? ? ? for (int i = 0; i < 30; i++) {
? ? ? ? ? ? // 上锁
? ? ? ? ? ? lock.lock();
? ? ? ? ? ? try { ?// 为了在后面写finally出口,因为finally必须跟try连用
? ? ? ? ? ? ? ? if (count <= 30) {
? ? ? ? ? ? ? ? ? ? String name = Thread.currentThread().getName(); // 获取当前线程的名字
? ? ? ? ? ? ? ? ? ? System.out.println(name + "窗口:卖出第" + count + "张票");
? ? ? ? ? ? ? ? ? ? count++;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? Thread.sleep(100);
? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ? throw new RuntimeException(e);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }finally { ?
? ? ? ? ? ? ? ? // 解锁
? ? ? ? ? ? ? ? lock.unlock();
? ? ? ? ? ? }
? ? ? ? }

? ? }
}

Timer类(定时器)

概念:在指定时间做指定事情,比如:闹钟

构造方法:

  1. Timer( ) : 创建一个新的计时器

  2. Timer( boolean isDaemon ) :创建一个新的定时器,其相关线程可以指定为 run as a daemon

  3. Timer(String name):创建一个新的定时器,其相关线程具有指定的名称

  4. Timer(String name, boolean isDaemon):创建一个新的定时器,其相关线程具有指定的名称,可以指定为 run as a daemon

常用方法:

  1. schedule(TimerTask task, Date time) :在指定的时间安排指定的任务执行。

  2. schedule(TimerTask task, long delay) 在指定的延迟之后安排指定的任务执行。

    timer.schedule(sayHi,2000); // 2秒后执行定时任务,一次性任务

  3. schedule(TimerTask task, Date firsttime, long period) :从指定的时间开始 ,对指定的任务执行重复的固定延迟执行 。 不支持过去的时间

    timer.schedule(sayHi,date,3000); // 从当前的时间开始,每隔3秒执行一次

  4. schedule(TimerTask task, long delay, long period) 在指定的延迟之后开始 ,重新执行固定延迟执行的指定任务。

    timer.schedule(sayHi,2000,3000); // 周期性的任务

  5. scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 从指定的时间开始 ,对指定的任务执行重复的固定速率执行 。

    timer.scheduleAtFixedRate(sayHi,date,3000);

  6. scheduleAtFixedRate(TimerTask task, long delay, long period) 在指定的延迟之后开始 ,重新执行固定速率的指定任务。

作用:1、定时发邮件 2、定时备份 3、定时写日志等

TimerTask:定时任务,另外一个线程 (public abstract class TimerTask extends Object implements Runnable)

文章来源:https://blog.csdn.net/Zyangxsir/article/details/135143204
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。