day15【自定义异常、线程、进程、多线程】 课上

1.自定义异常(理解)

概念介绍

自己定义异常类,对于一些异常类,sun公司并没有提供,那么我们只能自己定义异常类。

普通类
public class 类名{
    //成员位置
    //成员变量
    //成员方法
    //构造方法
    //构造代码块
    //静态代码块
    //成员内部类
}
异常类
public class 类名{
    
}
说明:
    1.自定义异常类必须继承Exception(编译时)/RuntimeException(运行时异常)
    2.自定义异常类的成员位置只需要书写构造方法(有参/无参),在构造方法中调用父类的构造方法
    3.我们自定义异常的目的就是报异常的时候可以做到具体的异常包具体的异常类,方便解决问题

练习(必须完成)

package com.itheima.sh.exception_01;

public class Retangle {
    //成员变量
    private double length;//长
    private double width;//宽

    public Retangle(double length, double width) throws IllegalLengthException, IllegalWidthException {
        /*
            判断长度是否合法,如果不合法则抛长度非法异常,快速结束程序
         */
        if(length <= 0){
            //非法
            throw new IllegalLengthException("长度必须大于0");
        }
        /*
            判读宽度是否合法,如果不合法则报宽度非法异常,快速结束程序
         */
        if(width <= 0){
            //非法
            throw new IllegalWidthException("宽度必须大于0");
        }
        this.length = length;
        this.width = width;
    }

    public Retangle() {
    }
    //方法计算面积
    public double getArea(){
        return this.length * this.width;
    }
}

package com.itheima.sh.exception_01;
/*
    练习:
    需求:定义一个类,描述矩形,提供计算面积的功能。要求对长和宽进行判断, 如果非法直接抛出长或宽非法异常。
 */
public class ExceptionTest01 {
    public static void main(String[] args) {
        //ctrl+alt+T 生成try-catch
        try {
            //1.创建对象
            Retangle r = new Retangle(2.0,-1.0);
            //2.调用方法计算矩形面积
            double area = r.getArea();
            System.out.println("area = " + area);
            //IllegalLengthException e = new IllegalLengthException("长度必须大于0");
        }catch (IllegalLengthException e) {
            e.printStackTrace();
        }catch (IllegalWidthException e){
            e.printStackTrace();
        }
    }
}

小结:

1.自定义异常类必须继承Exception或者RuntimeException

2.在自定义异常类中只书写构造方法即可,调用父类的构造方法

关于捕获多个异常注意事项

package com.itheima.sh.exception_02;

public class B extends A {
}


package com.itheima.sh.exception_02;

public class A extends Exception {
}



package com.itheima.sh.exception_02;

public class Test01 {
    public static void main(String[] args) {
        try {
            //调用方法
            method();
            /*
                Exception e =  new B();多态
                Exception e =  new A();多态
                Exception e =  new Exception();
             */
        } catch (B e) {//B e = new B();

        } catch (A e) {//A e = new A();

        } catch (Exception e) {//Exception e = new Exception();
            e.printStackTrace();
        }
    }

    private static void method() throws B, A, Exception {
        //定义变量
        int x = 0;
        if (x == 0) {
            //抛异常
            throw new B();
        }

        if (x == 1) {
            //抛异常
            throw new A();
        }

        if (x == 2) {
            //抛异常
            throw new Exception();
        }
    }
}

小结:

如果捕获多个异常,那么捕获异常的顺序应该是从小到大的顺序。最上面的catch获取最小的异常类,最下面的catch捕获最大的异常类

2.并发与并行(理解)

并发:某一时间段内。做某件事

并行:某一时刻。做某件事

在这里插入图片描述

吃饭和打电话只能是并发。

吃饭和打电话要想并行只能由两张嘴。

3.线程与进程(掌握)

  • 进程:正在进行的应用程序。就是你电脑打开的软件。每个进程都会在电脑内存开辟独立的空间的。
  • 线程:属于进程中一个单元,一个进程中最低要有一个线程。一般进程中都是多个线程运行的,因为多个线程运行效率高于单个线程。
    • 多线程好处:多线程就是一个进程中的多个线程同时运行,多个线程运行的效率要比单线程高。
    • java属于多线程,java中有一个主线程(main方法),java中还具有垃圾回收机制。

4.CPU调度(了解)

由于cpu在多个线程之间是随机切换的,具体执行哪个线程可以看哪个线程的优先级高就优先执行谁。

关于CPU调度有两种:

​ 1.分时调度:多个线程将cpu执行的时间平均分配

​ 2.抢占式调度:谁的优先级高就优先执行谁,java中属于抢占式调度。

5.使用java实现多线程(必须掌握)

在java中实现多线程有三种方式,今天先讲两种。在java中使用Thread类表示多线程。

1.Thread类介绍

  • 属于java.lang包下,不需要导包

  • 构造方法

    方法说明
    public Thread()创建线程对象,使用线程默认名字
    public Thread(String name)创建线程对象并指定线程名字
    public Thread(Runnable target)使用Runnable创建线程
    public Thread(Runnable target,String name)使用Runable创建线程并指定线程名字
  • 常用方法

    方法说明
    String getName()获取线程的名字
    void start()开启线程,这里调用了系统资源,让CPU执行代码。每个对象只调用一次start
    void run()run方法写线程执行的代码,此线程要执行的任务在此处定义代码
    static void sleep(long millis)让当前线程睡指定的时间
    static Thread currentThread()获取当前线程对象

2.实现多线程方式一_继承Thread类(掌握)

  • 实现步骤:

    • 自定义一个类继承线程类Thread,那么该子类也是线程类了
    • 在子类中重写Thread类中的run方法,书写多线程执行的任务
    • 创建子类即自定义线程类的对象
    • 使用子类对象调用Thread类中的start方法获取系统资源启动线程
      • 问题:书写任务的方法好像没有提到被调用,那怎么执行?
        • 对于任务方法run不需要我们单独调用,因为在启动线程即start方法内部已经帮助我们调用了Thread类中的run方法,但是子类已经重写了run方法,那么根据面向对象的特点,子类有,优先执行子类的run方法
  • 代码演示:

    package com.itheima.sh.thread_03;
    //1.自定义一个类继承线程类Thread,那么该子类也是线程类了
    public class MyThread extends Thread{
        //2.在子类中重写Thread类中的run方法,书写多线程执行的任务
        @Override
        public void run() {
            //书写任务
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
        }
    }
    
    package com.itheima.sh.thread_03;
    /*
        实现步骤:
        1.自定义一个类继承线程类Thread,那么该子类也是线程类了
        2.在子类中重写Thread类中的run方法,书写多线程执行的任务
        3.创建子类即自定义线程类的对象
        4.使用子类对象调用Thread类中的start方法获取系统资源**启动线程**
      - 问题:书写任务的方法好像没有提到被调用,那怎么执行?
        - 对于任务方法run不需要我们单独调用,因为在启动线程即**start**方法内部已经帮助我们调用了**Thread类中的run方法**,但是子类已经重写了run方法,那么根据面向对象的特点,子类有,优先执行子类的run方法
        0 1 2 3 4 5 6 7 8 9
    
        0 1 2 3 4 5 6 7 8 9
    
     */
    public class Test01 {
        public static void main(String[] args) {
            //3.创建子类即自定义线程类的对象
            MyThread mt = new MyThread();//mt属于线程对象
            //4.使用子类对象调用Thread类中的start方法获取系统资源**启动线程**
            //这里底层调用run方法
            mt.start();
    
            //在主线程这里执行任务
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
    
        }
    }
    
    
  • 小结:

    1.实现多线程方式一步骤:

    ​ 1.自定义类继承线程类Thread

    ​ 2.在自定义类中重写Thread类中的run方法

    ​ 3.创建自定义线程类对象

    ​ 4.使用自定义线程类对象调用start方法启动线程,底层调用任务方法run

    2.同一个线程不能启动多次否则会报异常:

    package com.itheima.sh.thread_03;
    /*
        实现步骤:
        1.自定义一个类继承线程类Thread,那么该子类也是线程类了
        2.在子类中重写Thread类中的run方法,书写多线程执行的任务
        3.创建子类即自定义线程类的对象
        4.使用子类对象调用Thread类中的start方法获取系统资源**启动线程**
      - 问题:书写任务的方法好像没有提到被调用,那怎么执行?
        - 对于任务方法run不需要我们单独调用,因为在启动线程即**start**方法内部已经帮助我们调用了**Thread类中的run方法**,但是子类已经重写了run方法,那么根据面向对象的特点,子类有,优先执行子类的run方法
        0 1 2 3 4 5 6 7 8 9
    
        0 1 2 3 4 5 6 7 8 9
    
     */
    public class Test01 {
        public static void main(String[] args) {
            //3.创建子类即自定义线程类的对象
            MyThread mt = new MyThread();//mt属于线程对象
            //4.使用子类对象调用Thread类中的start方法获取系统资源**启动线程**
            //这里底层调用run方法
            //同一个线程对象不能启动两次
            mt.start();
    //        mt.start();//报异常
            /*
                这里在第23行启动了第二次mt线程,发生异常了,导致主线程下面
                的所有代码都不会执行了,而在第22行已经启动了一个mt线程
                所以此时cpu会执行mt线程
             */
            //在主线程这里执行任务
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
            }
    
        }
    }
    
    

    3.获取、设置线程名称(掌握)

package com.itheima.sh.thread_04;
//1.自定义一个类继承线程类Thread,那么该子类也是线程类了
public class MyThread extends Thread{
    //2.在子类中重写Thread类中的run方法,书写多线程执行的任务
    @Override
    public void run() {
        //获取mt线程对象
        Thread mt = Thread.currentThread();
        //获取线程对象
        //自定义线程的默认名字是:Thread-x  x从0开始
        String name = mt.getName();
        //书写任务
        for (int i = 0; i < 10; i++) {
            System.out.println(i+"==="+name);
        }
    }
}

package com.itheima.sh.thread_04;
/*
    Thread类的方法:
        1.String getName()获取线程的名字
            该方法属于非静态方法,需要使用线程对象来调用
        2.static Thread currentThread()获取当前线程对象
        3. void setName(String name) 改变线程名称,使之与参数 name 相同。

 */
public class Test01 {
    public static void main(String[] args) {
        //3.创建子类即自定义线程类的对象
        MyThread mt = new MyThread();//mt属于线程对象
        //修改线程名字
        mt.setName("mt");
        //4.使用子类对象调用Thread类中的start方法获取系统资源**启动线程**
        //这里底层调用run方法
        mt.start();





        MyThread mt1 = new MyThread();
        //修改线程名字
        mt1.setName("mt1");
        mt1.start();




        //在主线程这里执行任务

        //获取主线程的对象
        //2.static Thread currentThread()获取当前线程对象
        Thread mainT = Thread.currentThread();
        //获取主线程的名字
        //主线程默认名字是 main
        String name = mainT.getName();
        for (int i = 0; i < 10; i++) {
            System.out.println(i+"---"+name);
        }

    }
}

小结:

1.String getName()获取线程的名字

​ 说明:主线程默认名字是main 自定义线程默认名字是Thread-x x从0开始递增

2.static Thread currentThread()获取当前线程对象

  1. void setName(String name) 改变线程名称,使之与参数 name 相同。

4.单线程和多线程区别:

在这里插入图片描述

4.实现多线程方式二(掌握)

  • 实现步骤

    • 自定义类实现Runnable接口
      • 由于Runnable中只有一个任务run方法,所以我们可以这样理解,该接口就是任务接口,用来完成任务的,以后我们可以将实现该接口的类称为任务类
    • 在自定义的任务类中实现Runnable接口中的run方法书写任务
    • 创建自定义任务类对象
    • 创建线程类Thread对象,然后上述的自定义任务类对象作为Thread的构造方法参数传递。
      • public Thread(Runnable target) 使用Runnable创建线程
      • public Thread(Runnable target,String name) 使用Runable创建线程并指定线程名字
    • 启动线程,调用Thread类的start方法
  • 代码实现

    package com.itheima.sh.runnable_05;
    //1.自定义类实现Runnable接口
    public class MyTask implements Runnable {
        //2.在自定义的任务类中实现**Runnable接口中的run方法**书写任务
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                //Thread.currentThread() 获取线程对象
                //getName() 获取线程名字
                System.out.println(Thread.currentThread().getName()+"----"+i);
            }
        }
    }
    
    
    package com.itheima.sh.runnable_05;
    /*
        实现步骤
    
        1.自定义类实现Runnable接口
            由于Runnable中只有一个任务run方法,所以我们可以这样理解,该接口就是任务接口,用来完成任务的,以后我们可以将实现该接口的类称为任务类
        2.在自定义的任务类中实现**Runnable接口中的run方法**书写任务
        3.创建自定义任务类对象
        4.创建线程类Thread对象,然后上述的自定义任务类对象作为Thread的构造方法参数传递。
          - public Thread(Runnable target)  使用Runnable创建线程
          - public Thread(Runnable target,String name)  使用Runable创建线程并指定线程名字
        5.启动线程,调用Thread类的start方法
     */
    public class Test01 {
        public static void main(String[] args) {
            // 3.创建自定义任务类对象
            MyTask mt = new MyTask();
            //public Thread(Runnable target,String name)
            Thread t1 = new Thread(mt, "t1");
            //5.启动线程,调用Thread类的start方法
            t1.start();
    
            //再次创建线程
            Thread t2 = new Thread(mt, "t2");
            t2.start();
        }
    }
    
    

    小结:

    1.步骤:

    ​ 1)创建任务类对象实现Runnable任务接口

    ​ 2)在任务类中实现任务方法run,书写任务

    ​ 3)创建任务类对象

    ​ 4)创建Thread线程类对象,任务对象作为构造方法参数传递

    ​ 5)使用线程对象调用start启动线程

5.关于实现多线程方式二_实现Runnable方式调用run方法疑问(掌握)

  • 继承Thread类的时候,自定义本身的父类是Thread,所以在调用start方法时调用run方法,子父类都有,优先执行子类的

  • 实现Runnable方式,自定义任务类和Thread没有关系,但是启动线程时调用start方法却执行了任务方法run.

    正常应该执行Thread类的run方法,那么原因是:

在这里插入图片描述

小结:

在Thread类的成员变量有一个target就是任务对象,然后在run方法中使用任务对象调用了任务类的run方法。

6.方式二好处(了解)

1.因为方式一采用了继承的方式,而java中类只支持单继承,如果一个类有了父类,那么这个类就不能采用方式一实现多线程。

2.方式二可以降低耦合度(降低依赖关系),方式二把任务和线程分开了。

7.使用匿名内部类实现多线程(掌握)

代码演示:

package com.itheima.sh.runnable_06;

/*
    使用匿名内部类方式实现多线程
    匿名内部类格式:
    new 父类或者父接口(){
        重写父类或者父接口方法
    };
 */
public class Test01 {
    public static void main(String[] args) {
        //创建子类对象
        //r就是任务类对象
      /*  Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(i);
                }
            }
        };
        //创建线程类的对象
        Thread t = new Thread(r);
        //启动线程
        t.start();

        Thread t1 = new Thread(r);
        //启动线程
        t1.start();*/

        //启动线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(i);
                }
            }
        }).start();
    }
}

6.线程控制_休眠(掌握)

package com.itheima.sh.thread_sleep_07;


import java.util.Date;

/*
    线程控制:
        使用Thread类的静态方法可以让线程休眠:
            static void sleep(long millis) 参数:millis表示休眠的时间,毫秒单位,当前线程遇到该方法,那么cpu此时就不会执行
                休眠时间一到自然醒
 */
public class Test01 {
    public static void main(String[] args) {
        //启动线程
        new Thread(new Runnable() {
            @Override
            public void run() {

                for (int i = 0; i < 10; i++) {
                    try {
                        //线程休眠
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(i+"---"+new Date());
                }
            }
        }).start();
    }
}

小结:

让线程休眠的方法:

 static void sleep(long millis) 参数:millis表示休眠的时间,毫秒单位,当前线程遇到该方法,那么cpu此时就不会执行. 休眠时间一到自然醒

7.高并发及线程安全(理解)

  • 高并发:在某一时间段内容大量的用户进行操作或者访问。例如双11买商品,12306买票
  • 线程安全:在高并发条件下,如果多个线程操作同一个变量(共享资源)时,由于线程的运行机制问题,会导致这个变量的结果有问题。

8.多线程的运行机制(掌握)

多线程运行的机制和之前学习的单线程机制是不一样的,只要运行一个线程,那么cpu就会给当前线程在内存中开辟一个独立的栈内存空间(栈空间),就是每个线程都有自己的栈空间。然后多个栈空间共享同一个堆空间和方法区。

在这里插入图片描述

9.多线程的安全性问题-可见性(掌握)

  • 代码演示:

    package com.itheima.sh.thread_safe_08;
    
    public class MyTask implements Runnable{
        //定义一个静态成员变量
        static int a = 0;
        @Override
        public void run() {
            try {
                //休眠
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改a
            a++;
            System.out.println("a被修改了为1");
        }
    }
    
    package com.itheima.sh.thread_safe_08;
    /*
        正常结果:
            a被修改了为1
            测试类获取到a等于1
        实际结果:
             a被修改了为1
             没有输出:测试类获取到a等于1
     */
    public class Test01 {
        public static void main(String[] args) {
            //创建任务对象
            MyTask mt = new MyTask();
            //创建线程
            Thread t = new Thread(mt);
            //启动线程
            t.start();
    
            while(true){
                if(MyTask.a == 1){
                    System.out.println("测试类获取到a等于1");
                }
            }
        }
    }
    
    
  • 图解

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃 哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值