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

目录

异常

线程

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)

Exception类(异常)- Thread类 (线程、多线程)- Timer类(定时器)_第1张图片

你可能感兴趣的:(开发语言,java)