【JavaSE】基础知识复习(三)

1.异常

 

Java异常体系

Throwable

Error和Exception

编译时异常和运行时异常

受检异常和非受检异常

常见的错误和异常

Error

运行时异常
import org.junit.Test;
import java.util.Scanner;
public class TestRuntimeException {
    @Test
    public void test01(){
        //NullPointerException
        int[][] arr = new int[3][];
        System.out.println(arr[0].length);
    }
    @Test
    public void test02(){
        //ClassCastException
        Object obj = 15;
        String str = (String) obj;
    }
    @Test
    public void test03(){
        //ArrayIndexOutOfBoundsException
        int[] arr = new int[5];
        for (int i = 1; i <= 5; i++) {
            System.out.println(arr[i]);
        }
    }
    @Test
    public void test04(){
        //InputMismatchException
        Scanner input = new Scanner(System.in);
        System.out.print("请输入一个整数:");//输入非整数
        int num = input.nextInt();
        input.close();
    }
    @Test
    public void test05(){
        int a = 1;
        int b = 0;
        //ArithmeticException
        System.out.println(a/b);
    }
}
编译时异常
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class TestCheckedException {
    @Test
    public void test06() {
        Thread.sleep(1000);//休眠 1 秒 InterruptedException
    }
    @Test
    public void test07(){
        Class c = Class.forName("java.lang.String");//ClassNotFoundEx
        ception
    }
    @Test
    public void test08() {
        Connection conn = DriverManager.getConnection("...."); //SQL
        Exception
    }
    @Test
    public void test09() {
        FileInputStream fis = new FileInputStream("尚硅谷 Java 秘籍.txt
                "); //FileNotFoundException
    }
    @Test
    public void test10() {
        File file = new File("尚硅谷 Java 秘籍.txt");
        FileInputStream fis = new FileInputStream(file);//FileNotFound
        Exception
        int b = fis.read();//IOException
        while(b != -1){
            System.out.print((char)b);
            b = fis.read();//IOException
        }
        fis.close();//IOException
    }
}

异常的处理

Java异常处理(两种方式)

try-catch-finally捕获异常

确保资源关闭
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestFinally {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入第一个整数:");
            int a = input.nextInt();
            System.out.print("请输入第二个整数:");
            int b = input.nextInt();
            int result = a/b;
            System.out.println(a + "/" + b +"=" + result);
        } catch (InputMismatchException e) {
            System.out.println("数字格式不正确,请输入两个整数");
        }catch (ArithmeticException e){
            System.out.println("第二个整数不能为 0");
        } finally {
            System.out.println("程序结束,释放资源");
            input.close();
        }
    }

    @Test
    public void test1(){
        FileInputStream fis = null;
        try{
            File file = new File("hello1.txt");
            fis = new FileInputStream(file);//FileNotFoundException
            int b = fis.read();//IOException
            while(b != -1){
                System.out.print((char)b);
                b = fis.read();//IOException
            }
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            try {
                if(fis != null)
                    fis.close();//IOException
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

throws抛出异常

import java.util.InputMismatchException;
import java.util.Scanner;
public class TestThrowsRuntimeException {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入第一个整数:");
            int a = input.nextInt();
            System.out.print("请输入第二个整数:");
            int b = input.nextInt();
            int result = divide(a,b);
            System.out.println(a + "/" + b +"=" + result);
        } catch (ArithmeticException | InputMismatchException e) {
            e.printStackTrace();
        } finally {
            input.close();
        }
    }
    public static int divide(int a, int b)throws ArithmeticException{
        return a/b;
    }
}
方法重写中对throws的要求

两种异常处理方式的选择

手动抛出异常对象:throw

使用格式

注意点

自定义异常

注意点

举例 1:
class MyException extends Exception {
    static final long serialVersionUID = 23423423435L;
    private int idnumber;
    public MyException(String message, int id) {
        super(message);
        this.idnumber = id;
    }
    public int getId() {
        return idnumber;
    }
}
public class MyExpTest {
    public void regist(int num) throws MyException {
        if (num < 0)
            throw new MyException("人数为负值,不合理", 3);
        else
            System.out.println("登记人数" + num);
    }
    public void manager() {
        try {
            regist(100);
        } catch (MyException e) {
            System.out.print("登记失败,出错种类" + e.getId());
        }
        System.out.print("本次登记操作结束");
    }
    public static void main(String args[]) {
        MyExpTest t = new MyExpTest();
        t.manager();
    }
}

小结


2.多线程

概念

程序、进程与线程

其实不用背那么拗口的概念,一句话,有了多线程,可以让程序在同一时间做多件事情

简单理解线程:应用软件中互相独立,可以同时运行的功能

查看进程和线程
  • 每个应用程序的运行都是一个进程

  • 一个应用程序的多次运行,就是多个进程

  • 一个进程中包含多个线程

线程调度

多线程程序的优点

 并发和并行

  • 并发与并行有可能同时在发生

多线程的实现方式(三种)

(1)继承Thread类

(2)实现Runnable接口

(3)利用Callable接口和Future接口

前两种方法重写run()都是void无返回值的,无法获得run()运行的结果,而 ...

多线程中的常用成员方法

线程名称

可以通过setName设置名称

可以通过构造器设置名称

默认名称是Thread-X(X是需要,从0开始的)

可以通过getName获取名称

当JVM虚拟机启动之后,会自动的启动多条线程

其中有一条线程就叫做main线程

他的作用就是去调用main方法,并执行里面的代码

在以前,我们所写的代码,都是运行在main线程当中

获取当前线程对象

线程休眠

线程优先级

Java中,线程优先级默认是5,最小是1,最大是10

  • 优先级越大,抢占cpu的概率越高
  • 记住,只是概率越高,而不是百分百能抢到cpu的执行权,多运行几次试试看

线程调度

在计算机当中,线程调度分为 抢占式调度非抢占式调度

  • 抢占式调度(随机性):多个线程抢夺cpu的执行权,cpu在同一时刻执行哪一个线程是不确定的,执行多长时间也是不确定的
  • 非抢占式调度(有序性):多个线程轮流的执行,你一次我一次,执行的时间也是差不多的

在Java中,采用的是抢占式调度

守护线程

非守护线程执行完毕后,守护线程没有存在的必要,会陆续结束

如何理解陆续结束?

        比如女神线程打印100个数,守护线程也打印100个数,当女神线程打印完后,守护线程没有存在的必要了就会自动进入死亡状态,但是在进入之前的一瞬间还是能执行部分线程程序的,比如打印了30个数就结束了

礼让线程

为了让多个线程抢占cpu执行权,概率尽量均匀一些

注意不是完全均匀!!

插队线程

线程的生命周期

就绪状态:有抢cpu的资格但是还没抢到,所以没有执行权

其实应该还有wait和notify还有阻塞与锁,这里老师并没有讲,不知道为啥

同步代码块

将操作共享数据的代码块锁起来,让所有线程轮流执行这段代码块

多线程处理同一数据:多窗口售票
public class MyThread extends Thread {
    // 被多线程操作的共享数据
    static int ticket = 0;// 0 ~ 99
    @Override
    public void run() {
         while (true) {
             if (ticket < 10) {
                 try {
                     Thread.sleep(100);
                 } catch (InterruptedException e) {
                     throw new RuntimeException(e);
                 }
                 ticket++;
                 System.out.println(getName() + "售出第:" + ticket + "张门票。");
             }else {
                 break;
             }
         }
    }
}
public class MyTest {
    public static void main(String[] args) {
        /*
            需求:
                某电影院目前正在上映国产大片,共有100张票,而有三个售票窗口,模拟程序。
         */
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

代码逻辑上似乎没问题,每次某个窗口卖出一张票时,就会调用sleep()睡眠100ms,避免卖票太快,但是运行结果发现

问题思考

多线程处理同一数据引发的问题:

  1. 相同的数据出现了多次
  2. 出现了超出范围的数据
解决思路

当有一个线程抢到执行权,就锁住这块代码,就算其他线程也抢到了执行权,也要等着,等执行完,其他线程才能轮流进入

将线程的随机性变成有序性

Synchronized锁

锁对象,一定要是唯一的,确定锁的唯一

修改源代码(两种方式)

修改上文源代码

① 继承Thread类
public class MyTest {
    public static void main(String[] args) {
        /*
            需求:
                某电影院目前正在上映国产大片,共有100张票,而有三个售票窗口,模拟程序。
         */
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
public class MyThread extends Thread {
    // 被多线程操作的共享数据
    static int ticket = 0;// 0 ~ 99

    // 锁对象,一定要是唯一的
    static Object obj = new Object();

    @Override
    public void run() {
         while (true) {
             // 同步代码块
             synchronized (obj) {
                 if (ticket < 100) {
                     try {
                         Thread.sleep(10);
                     } catch (InterruptedException e) {
                         throw new RuntimeException(e);
                     }
                     ticket++;
                     System.out.println(getName() + "售出第:" + ticket + "张门票。");
                 }else {
                     break;
                 }
             }
         }
    }
}

② 实现Runnable接口
public class MyTest {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        Thread t3 = new Thread(mr);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
public class MyRunnable implements Runnable{
    // 这种实现方式反正也就创建一次对象,不需要static修饰
    int ticket = 0;

    @Override
    public void run() {
        // 1.循环
        while (true) {
            // 2.同步代码块(同步方法)
            synchronized (MyRunnable.class) {
                // 3.判断共享数据是否到了末尾,如果到了末尾
                if (ticket == 100) {
                    break;
                }else {
                    // 4.如果没到末尾
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    ticket++;
                    System.out.println(Thread.currentThread().getName() + "售出" + ticket + "张票。");
                }
            }
        }
    }
}

同步方法

给上文代码抽取成方法

public class MyRunnable implements Runnable{
    // 这种实现方式反正也就创建一次对象,不需要static修饰
    int ticket = 0;

    @Override
    public void run() {
        // 1.循环
        while (true) {
            // 2.同步代码块(同步方法)
            if (method()) break;
        }
    }

    // 是非静态方法,锁对象是this
    private synchronized boolean method() {
        // 3.判断共享数据是否到了末尾,如果到了末尾
        if (ticket == 100) {
            return true;
        }else {
            // 4.如果没到末尾
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            ticket++;
            System.out.println(Thread.currentThread().getName() + "售出" + ticket + "张票。");
        }
        return false;
    }
}
public class MyTest {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        Thread t3 = new Thread(mr);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

线程销毁

Lock锁

Synchronized锁的上锁和释放锁都是自动的,Lock锁提供手动上锁手动释放锁

下面这个例子的代码逻辑错了,不过lock的使用没问题可以看

多线程&JUC-16-lock锁_哔哩哔哩_bilibili

public class MyThread extends Thread{
    static int ticket = 0;

//    static Object obj = new Object();

    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();
//            synchronized (obj) {
            try {
                // 同步代码块
                if (ticket < 100) {
                        Thread.sleep(30);
                    ticket++;
                    System.out.println(getName() + "售出第:" + ticket + "张门票。");
                }else {
                    break;
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
//            }
        }
    }
}
package com.autism;

/**
 * @Author oi
 * @Date 2024/8/4 5:01
 * @Description:
 */
public class MyTest {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

死锁(一种错误)

两个线程分别拿着自己的锁,并且等着对方释放锁,从而卡住

锁的嵌套容易导致死锁

等待唤醒机制(两种)

生产者/消费者实现

Java的线程调度是抢占式调度,具有随机性,生产者消费者模式将打破这种机制,变成轮流执行,你一次我一次

理想情况
  • 厨师抢到了CPU的执行权,发现桌上空的,厨师将饭端上桌,释放线程
  • 顾客抢到了CPU执行权,顾客吃饭

厨师端一碗,顾客吃一碗,厨师再端一碗,顾客再吃一碗

消费者等待(wait&notify)

但是Java的线程调度具有随机性,程序不可能这么听话,不总会按照我们想着的这么听话

  • 顾客先抢到了CPU的执行权,发现桌上空的,于是进入等待状态(wait)
  • 此时厨师就可以抢到CPU的执行权,发现桌上空的,端上了一碗饭
  • 可惜此时顾客依旧处于wait,厨师还需要一次唤醒(notify),提醒顾客吃饭

生产者等待
  • 厨师先抢到了CPU的执行权,发现桌上空的,端上一碗饭,唤醒(notify)
  • 厨师02紧跟着抢到了CPU的执行权,发现桌上有饭,只能等着(wait)
完整的生产者消费者机制

常见方法

代码实现
消费者代码实现

代码实现逻辑是有问题的,看看方法使用就好

public class Foodie extends Thread{
    @Override
    public void run() {
        while (true) {
            //同步代码块
            synchronized (Desk.lock) {
                if (Desk.count == 0) {
                    break;
                }else {
                    if (Desk.foodFlag == 0) {
                        // 如果没有,则等待
                        try {
                            Desk.lock.wait(); // 让当前线程跟锁进行绑定
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        // 如果有,则吃掉一碗
                        Desk.count--;
                        System.out.println("顾客吃了一碗面条,还剩下" + Desk.count + "碗。");
                        // 吃完之后,唤醒厨师继续做
                        Desk.lock.notifyAll();
                        // 修改桌子的状态
                        Desk.foodFlag = 0;
                    }
                }
            }
        }
    }
}
public class Desk {
    /**
     * 作用:控制生产者和消费者的执行
     *
     */

    // 是否有面条    0:没有面条  1:有面条
    public static int foodFlag = 0;

    // 总个数 (模拟判断条件)
    public static int count = 10;

    // 锁对象
    public static Object lock = new Object();
}

生产者代码实现
public class Cook extends Thread{
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                if (Desk.count == 0) {
                    break;
                }else {
                    if (Desk.foodFlag == 1) {
                        // 有,就等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        // 没有,就制作食物
                        System.out.println("制作了一碗");
                        // 放在桌子上(修改桌子上的食物状态)
                        Desk.foodFlag = 1;
                        // 叫醒等待的消费者开吃
                        Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}
public class Desk {
    /**
     * 作用:控制生产者和消费者的执行
     *
     */

    // 是否有面条    0:没有面条  1:有面条
    public static int foodFlag = 0;

    // 总个数 (模拟判断条件)
    public static int count = 10;

    // 锁对象
    public static Object lock = new Object();
}
编写测试类
public class MyTest {
    public static void main(String[] args) {
        /**
         *
         *  需求:完成生产者和消费者(等待唤醒机制)的代码
         *        实现线程轮流交替执行的效果
         *
         */

        // 创建线程的对象
        Cook c = new Cook();
        Foodie f = new Foodie();

        // 给线程设置名字
        c.setName("厨师");
        f.setName("顾客");

        // 开启线程
        c.start();
        f.start();
    }
}

阻塞队列实现
阻塞与队列
  • 理解阻塞?

  • 理解队列

数据在队列中,像是在排队一样,先进先出,后进后出,是可以设置最大值的

阻塞队列的继承结构

代码案例

  • 测试类

在测试类中再创建阻塞队列的对象

注意此时输出语句是在锁的外面的,所以输出可能是乱的,但是程序本身没有错误

多线程的四步套路

建议按照这个步骤写多线程,实用率高

多线程的六大状态

Java中并没有运行这个状态,是黑马的阿伟老师自己加的

为什么Java的线程没有运行状态?

当线程抢到CPU执行权的时候,JVM便将此线程交给操作系统,自己不管了


多线程练习题

第一题

public class TicketWindow extends Thread{
    // 1000张电影票
    static int ticketCount = 1000;

    // lock锁
    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                // 同步代码块
                if (ticketCount == 0) {
                    break;
                }else {
                    Thread.sleep(3000);
                    ticketCount--;
                    System.out.println(getName() + "卖出了1张票,还剩:" + ticketCount + "张票。");
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
        }
    }
}
public class MyTest {
    public static void main(String[] args) {
        TicketWindow tw1 = new TicketWindow();
        TicketWindow tw2 = new TicketWindow();
        TicketWindow tw3 = new TicketWindow();

        tw1.setName("①号售票窗口");
        tw2.setName("②号售票窗口");
        tw3.setName("③号售票窗口");

        tw1.start();
        tw2.start();
        tw3.start();
    }
}
第二题

public class Gift extends Thread{
    static int giftCount = 100;
    static Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                // 同步代码块
                if (giftCount < 10) {
                    break;
                }else {
                    giftCount--;
                    System.out.println(getName() + "送出了1份礼物,还剩下:" + giftCount + "份。");
                }
            }
        }
    }
}
public class MyTest {
    public static void main(String[] args) {
        Gift g1 = new Gift();
        Gift g2 = new Gift();

        g1.setName("①号送礼人");
        g2.setName("②号送礼人");

        g1.start();
        g2.start();
    }
}
第三题

public class Number extends Thread{
    static int number = 1;
    static Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (number == 100) {
                    break;
                }else {
                    if (number % 2 == 1) {
                        System.out.println(getName() + "输出奇数:" + number);
                    }
                    number++;
                }
            }
        }
    }
}
public class MyTest {
    public static void main(String[] args) {
        Number n1 = new Number();
        Number n2 = new Number();

        n1.setName("线程1");
        n2.setName("线程2");

        n1.start();
        n2.start();
    }
}
第四题

用double实现

这种是有精度损失的,主要是便于新手看代码逻辑,BigDecimal的加减乘除都是用方法实现的不方便看

开发中,有关金额的处理绝对不可以使用double,绝对不可以,绝对不行

public class RedPacket extends Thread{
    // 总金额100元
    static double money = 100;
    // 分成三个红包
    static int packetCount = 3;

    // 最小的中奖金额:0.01元
    static final double MIN_MONEY = 0.01;

    static Object obj = new Object();
    @Override
    public void run() {
        // 循环(一个人只能抢一次,so没必要循环)
        synchronized (obj) {
            // 同步代码块
            if (packetCount == 0) {
                // 判断,共享数据是否到了末尾(已经到了末尾)
                System.out.println(getName() + "没有抢到红包!");
            }else {
                // 判断,共享数据是否到了末尾(没有到末尾)
                // 定义一个变量,表示中奖的金额
                double prize = 0;
                if (packetCount == 1) {
                    // 此时只剩下最后一个红包
                    // 最值范围:【0.01, (100 - 第一个 - 第二个)】
                    // 无需随机,剩余的钱都是中间金额
                    prize = money;
                }else {
                    // 表示第一次,第二次(随机)
                    Random r = new Random();
                    // 100元 3个红包
                    // 第一个红包的最值范围:【0.01,99.98】
                    // 99.98 = 100 - (3-1) * 0.01
                    double bounds = money - (packetCount - 1) * MIN_MONEY;
                    prize = r.nextDouble(bounds);
                    if (prize < MIN_MONEY) {
                        // 如果小于0.01,则算做0.01
                        prize = MIN_MONEY;
                    }
                }
                // 从money当中,去掉当前中奖的金额
                money = money - prize;
                // 红包的个数 - 1
                packetCount--;
                System.out.println(getName() + "抢到了" + prize + "元。");
            }
        }
    }
}
public class MyTest {
    public static void main(String[] args) {
        RedPacket rp1 = new RedPacket();
        RedPacket rp2 = new RedPacket();
        RedPacket rp3 = new RedPacket();
        RedPacket rp4 = new RedPacket();
        RedPacket rp5 = new RedPacket();

        rp1.setName("用户1");
        rp2.setName("用户2");
        rp3.setName("用户3");
        rp4.setName("用户4");
        rp5.setName("用户5");

        rp1.start();
        rp2.start();
        rp3.start();
        rp4.start();
        rp5.start();
    }
}

用BigDecimal实现

使用BigDecimal实现可以解决精度问题,不过阿伟老师的如下给出的BigDecimal其实依旧没有解决精度问题开发中禁止使用将double直接转为BigDecimal

应该先转为字符串,再去处理,底层使用数组存储每一位,进行位运算,即可避免精度损失

常用API-08-BigDecima基本使用和原理解析_哔哩哔哩_bilibili

public class RedPacket extends Thread{
    // 总金额100元
    static BigDecimal money = BigDecimal.valueOf(100.0);
    // 分成三个红包
    static int packetCount = 3;

    // 最小的中奖金额:0.01元
    static final BigDecimal MIN_MONEY = BigDecimal.valueOf(0.01);

    static Object obj = new Object();
    @Override
    public void run() {
        // 循环(一个人只能抢一次,so没必要循环)
        synchronized (obj) {
            // 同步代码块
            if (packetCount == 0) {
                // 判断,共享数据是否到了末尾(已经到了末尾)
                System.out.println(getName() + "没有抢到红包!");
            }else {
                // 判断,共享数据是否到了末尾(没有到末尾)
                // 定义一个变量,表示中奖的金额
                BigDecimal prize;
                if (packetCount == 1) {
                    // 此时只剩下最后一个红包
                    // 最值范围:【0.01, (100 - 第一个 - 第二个)】
                    // 无需随机,剩余的钱都是中间金额
                    prize = money;
                }else {
                    // 表示第一次,第二次(随机)
                    Random r = new Random();
                    // 100元 3个红包
                    // 第一个红包的最值范围:【0.01,99.98】
                    // 99.98 = 100 - (3-1) * 0.01
//                    double bounds = money - (packetCount - 1) * MIN_MONEY;
                    double bounds = money.subtract(BigDecimal.valueOf(packetCount - 1).multiply(MIN_MONEY)).doubleValue();
//                    prize = r.nextDouble(bounds);
                    prize = BigDecimal.valueOf(r.nextDouble(bounds));
                }
                //设置抽中红包,小数点保留两位,四舍五入
                prize = prize.setScale(2, RoundingMode.HALF_UP);
                // 从money当中,去掉当前中奖的金额
//                money = money - prize;
                money = money.subtract(prize);
                // 红包的个数 - 1
                packetCount--;
                System.out.println(getName() + "抢到了" + prize + "元。");
            }
        }
    }
}
public class MyTest {
    public static void main(String[] args) {
        RedPacket rp1 = new RedPacket();
        RedPacket rp2 = new RedPacket();
        RedPacket rp3 = new RedPacket();
        RedPacket rp4 = new RedPacket();
        RedPacket rp5 = new RedPacket();

        rp1.setName("用户1");
        rp2.setName("用户2");
        rp3.setName("用户3");
        rp4.setName("用户4");
        rp5.setName("用户5");

        rp1.start();
        rp2.start();
        rp3.start();
        rp4.start();
        rp5.start();
    }
}

第五题

第六题

第七题

第八题

线程池

线程不释放,线程复用

发现复用了线程1

常见方法

两个Java自带的工具类

具体实现

public class MyThreadPool {
    public static void main(String[] args) {
        // 1.获取线程池对象
        ExecutorService pool1 = Executors.newCachedThreadPool();
//        ExecutorService pool2 = Executors.newFixedThreadPool(3);

        // 2.提交任务
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());

        // 3.销毁线程池 (一般不会销毁)
//        pool1.shutdown();
    }
}
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}

自定义线程池

Java自带的工具类Excutors不够灵活

ThreadPoolExecutor实现类

查看Executors源码,其中一个实现类ThreadPoolExecutor的构造器有7个参数

理解七个参数

​​​​​​​

线程池处理逻辑(重要)

核心线程:3

临时线程:3

阻塞队列长度:3

此时提交了10个任务

会发生什么事情呢?

  1. ​​​​​​​​​​​​​​首先核心线程1~3立即处理任务1~3
  2. 其次创建阻塞队列让任务4~6等待,等着核心线程1~3来处理
  3. 然后创建临时线程4~6立即处理任务7~9
  4. 3(核心线程) + 3(临时线程)+ 3(队列长度) < 10,则第10个任务被拒绝服务,除法任务拒绝策略
任务拒绝策略

代码实现

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值