37-多线程综合练习

多线程综合练习

一、卖电影票

需求
  • 一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为3000毫秒。
  • 要求:请用多线程模拟卖票过程并打印剩余电影票的数量。

实现
  • 自定义卖电影票的线程类

    package com.app.demo41_multithreading_test.test1;
    
    /*
        自定义卖电影票的线程类
     */
    public class MyThread extends Thread{
        // 第一种方式实现多线程,测试类中MyThread会创建多次,所以需要用static修饰ticket变量
        // 一共有1000张电影票
        static int ticket = 1000;
    
        // 重写run方法
        @Override
        public void run() {
            //1.循环
            while (true) {
                //2.同步代码块
                synchronized (MyThread.class) {
                    //3.判断共享数据(已到末尾)
                    if (ticket == 0) {
                        break;
                    }else {
                        //4.判断共享数据(未到末尾)
                        // 每次领取的时间为3000毫秒
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        // 当每次被领取的时候,电影票总数-1
                        ticket--;
                        System.out.println(getName() + "在卖电影票,目前还剩下" + ticket + "张电影票~");
                    }
                }
            }
        }
    }
    
  • 测试类

    package com.app.demo41_multithreading_test.test1;
    
    public class Test {
        public static void main(String[] args) {
            /*
                多线程综合练习:卖电影票
                  需求:
                    一共有1000张电影票,可以在两个窗口领取,假设每次领取的时间为3000毫秒。
                    要求:请用多线程模拟卖票过程并打印剩余电影票的数量。
             */
            // 1.创建两个线程对象:模拟两个售票窗口
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();
            
            // 2.给两个线程设置名称
            t1.setName("窗口1");
            t2.setName("窗口2");
    
            // 3.启动两个线程
            t1.start();
            t2.start();
        }
    }
    
  • 运行结果(输出语句是随机打印的,只要不是手动停止运行,那就当票数为0时,程序才会停止运行):

    窗口1在卖电影票,目前还剩下999张电影票~
    窗口2在卖电影票,目前还剩下998张电影票~
    窗口2在卖电影票,目前还剩下997张电影票~
    窗口1在卖电影票,目前还剩下996张电影票~
    窗口1在卖电影票,目前还剩下995张电影票~
    窗口2在卖电影票,目前还剩下994张电影票~
    窗口2在卖电影票,目前还剩下993张电影票~
    窗口2在卖电影票,目前还剩下992张电影票~
    ......
    窗口1在卖电影票,目前还剩下1张电影票~
    窗口1在卖电影票,目前还剩下0张电影票~
    
    Process finished with exit code 0
    
    


二、送礼品

需求
  • 有100份礼品,两人同时发送,当剩下的礼品小于10份的时候则不再送出。
  • 要求:利用多线程模拟该过程并将线程的名字和礼物的剩余数量打印出来。

实现
  • 自定义送礼品的线程类

    package com.app.demo41_multithreading_test.test2;
    
    import com.app.demo41_multithreading_test.test1.MyThread;
    
    /*
        自定义送礼品的线程类
     */
    public class MyRunnable implements Runnable {
        // 第二种方式实现多线程,测试类中MyRunnable只会创建1次,所以不需要用static修饰gift变量
        //一共有100份礼品
        int gift = 100;
    
        //重写run方法
        @Override
        public void run() {
            //1.循环
            while (true) {
                //2.同步代码块
                synchronized (MyThread.class) {
                    //3.判断共享数据(已到末尾)
                    if (gift < 10) {
                        System.out.println("礼品还剩下" + gift + "个,不再赠送~");
                        break;
                    }else {
                        //4.判断共享数据(未到末尾)
                        // 送出礼品,礼品总数-1
                        gift--;
                        System.out.println(Thread.currentThread().getName() + "在赠送礼品,还剩下" + gift + "个~");
                    }
                }
            }
        }
    }
    
  • 测试类

    package com.app.demo41_multithreading_test.test2;
    
    public class Test {
        public static void main(String[] args) {
            /*
                多线程综合练习:送礼品
                  需求:
                    有100份礼品,两人同时发送,当剩下的礼品小于10份的时候则不再送出。
                    要求:利用多线程模拟该过程并将线程的名字和礼物的剩余数量打印出来。
             */
            //1.创建参数对象
            MyRunnable mr = new MyRunnable();
    
            //2.创建两个线程对象
            Thread t1 = new Thread(mr, "周杰伦");
            Thread t2 = new Thread(mr, "刘亦菲");
    
            //3.启动两个线程
            t1.start();
            t2.start();
        }
    }
    
  • 运行结果

    周杰伦在赠送礼品,还剩下99个~
    周杰伦在赠送礼品,还剩下98个~
    周杰伦在赠送礼品,还剩下97个~
    周杰伦在赠送礼品,还剩下96个~
    周杰伦在赠送礼品,还剩下95个~
    周杰伦在赠送礼品,还剩下94个~
    ......
    刘亦菲在赠送礼品,还剩下63个~
    刘亦菲在赠送礼品,还剩下62个~
    刘亦菲在赠送礼品,还剩下61个~
    刘亦菲在赠送礼品,还剩下60个~
    刘亦菲在赠送礼品,还剩下59个~
    刘亦菲在赠送礼品,还剩下58个~
    刘亦菲在赠送礼品,还剩下57个~
    刘亦菲在赠送礼品,还剩下56个~
    刘亦菲在赠送礼品,还剩下55个~
    刘亦菲在赠送礼品,还剩下54个~
    ......
    礼品还剩下9个,不再赠送~
    礼品还剩下9个,不再赠送~
    
    Process finished with exit code 0
    
    


三、打印奇数数字

需求
  • 同时开启两个线程,共同获取1-100之间的所有数字。
  • 要求:将输出所有的奇数。

实现
  • 自定义打印1~100之间所有奇数的线程类

    package com.app.demo41_multithreading_test.test3;
    
    import com.app.demo41_multithreading_test.test1.MyThread;
    
    /*
        自定义打印奇数数字的线程类
     */
    public class MyRunnable implements Runnable{
        //第二种方式实现多线程,测试类中MyRunnable只会创建1次,所以不需要用static修饰number变量
        //定义数字1,表示从1开始
        int number = 1;
    
        //重写run方法
        @Override
        public void run() {
            //1.循环
            while (true) {
                //2.同步代码块
                synchronized (Thread.class) {
                    //3.判断共享数据(已到末尾)
                    if (number > 100) {
                        break;
                    }else {
                        //4.判断共享数据(未到末尾)
                        // 1~100之间的每一个数对2进行求余数,余数为1就是奇数
                        if (number % 2 == 1) {
                            System.out.println(Thread.currentThread().getName() + "打印数字:" + number);
                        }
                        // 让number自增,表示从1到2、3、4、5...100
                        number++;
                    }
                }
            }
        }
    }
    
  • 测试类

    package com.app.demo41_multithreading_test.test3;
    
    public class Test {
        public static void main(String[] args) {
            /*
                多线程综合练习:打印奇数数字
                   需求:
                    同时开启两个线程,共同获取1-100之间的所有数字。
                    要求:将输出所有的奇数。
             */
            //1.创建参数对象
            MyRunnable mr = new MyRunnable();
    
            //2.创建两个线程对象
            Thread t1 = new Thread(mr, "线程A");
            Thread t2 = new Thread(mr, "线程B");
    
            //3.启动两个线程
            t1.start();
            t2.start();
        }
    }
    
  • 运行结果

    线程A打印数字:1
    线程A打印数字:3
    线程A打印数字:5
    线程A打印数字:7
    线程A打印数字:9
    线程A打印数字:11
    线程A打印数字:13
    线程A打印数字:15
    线程A打印数字:17
    线程A打印数字:19
    线程A打印数字:21
    线程A打印数字:23
    线程A打印数字:25
    线程A打印数字:27
    线程A打印数字:29
    线程A打印数字:31
    线程A打印数字:33
    线程A打印数字:35
    线程A打印数字:37
    线程A打印数字:39
    线程A打印数字:41
    线程B打印数字:43
    线程B打印数字:45
    线程B打印数字:47
    线程B打印数字:49
    线程B打印数字:51
    线程B打印数字:53
    线程B打印数字:55
    线程B打印数字:57
    线程B打印数字:59
    线程B打印数字:61
    线程B打印数字:63
    线程B打印数字:65
    线程B打印数字:67
    线程B打印数字:69
    线程A打印数字:71
    线程A打印数字:73
    线程A打印数字:75
    线程A打印数字:77
    线程A打印数字:79
    线程A打印数字:81
    线程A打印数字:83
    线程A打印数字:85
    线程A打印数字:87
    线程A打印数字:89
    线程A打印数字:91
    线程A打印数字:93
    线程A打印数字:95
    线程A打印数字:97
    线程A打印数字:99
    
    Process finished with exit code 0
    
    


四、抢红包

需求
  • 假设:100元,分成3个红包,现在有5个人去抢。

  • 要求:红包是共享数据;5个人是5条线程。

  • 打印结果如下:

    XXX抢到了XXX元
    XXX抢到了XXX元
    XXX抢到了XXX元
    XXX没抢到
    XXX没抢到
    

实现(不考虑保留小数位)
  • 自定义抢红包的线程类

    package com.app.demo41_multithreading_test.test4;
    
    import java.util.ArrayList;
    import java.util.Random;
    
    /*
        自定义抢红包的线程类
     */
    public class MyThread extends Thread {
        // 共享数据:100元、3个红包
        static double money = 100;
        static int count = 3;
    
        // 定义最小红包的金额:0.01元
        static final double MIN_MONEY = 0.01;
    
        // 重写run方法
        @Override
        public void run() {
            //1.循环:在当前业务中用不上,因为每个人只能抢一次红包
            //2.同步代码块
            synchronized (MyThread.class) {
                //3.判断共享数据(已经到末尾)
                if (count == 0) { // 3个红包已全部抢完
                    System.out.println(getName() + "没有抢到红包~");
                } else {
                    //4.判断共享数据(没有到末尾)
                    // 定义变量,用于记录抢到的红包金额
                    double prize = 0;
                    // 判断目前抢的是否为最后一个红包
                    if (count == 1) {
                        // 是,则直接将剩余的金额给出去
                        prize = money;
                    } else {
                        // 否,则随机99.98以内的数额给出去
                        // 创建随机数对象
                        Random r = new Random();
                        // 分析:因为如果随机100以内的数额,有可能会直接将100给随机出去了,这样就不符合需求了
                        // 所以要留两个0.01元做最小金额
                        // 计算:100 - 2*0.01 == 99.98
                        double bound = money - (count - 1) * MIN_MONEY;
                        // 随机一个99.98元以内的红包
                        // 用于存储0.01~99.98内的所有红包金额
                        ArrayList<Double> list = new ArrayList<>();
                        for (double i = MIN_MONEY; i <= bound;i+=0.01) {
                            list.add(i);
                        }
                        // 随机一个集合长度内的索引
                        int index = r.nextInt(list.size());
                        // 根据这个随机索引,从红包集合list中随机一个红包金额出来
                        prize = list.get(index);
    
                        /*// 判断随机的红包金额是否小于最小红包金额0.01
                        if (prize < MIN_MONEY) {
                            // 小于,则直接将0.00元改为0.01元
                            prize = MIN_MONEY;
                        }*/
                    }
                    // 计算总金额剩余多少?
                    // 剩余金额 = 总金额 - 随机红包金额
                    money = money - prize;
                    // 红包的个数-1
                    count--;
                    // 输出提示
                    System.out.println(getName() + "抢到了" + prize + "元");
                }
            }
        }
    }
    
  • 测试类

    package com.app.demo41_multithreading_test.test4;
    
    public class Test {
        public static void main(String[] args) {
            /*
                多线程综合练习:抢红包
                   需求:
                     假设:100元,分成3个红包,现在有5个人去抢。
                     要求:红包是共享数据;5个人是5条线程。
                     打印结果如下:
                      XXX抢到了XXX元
                      XXX抢到了XXX元
                      XXX抢到了XXX元
                      XXX没抢到
                      XXX没抢到
             */
            // 创建5条线程
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();
            MyThread t3 = new MyThread();
            MyThread t4 = new MyThread();
            MyThread t5 = new MyThread();
    
            // 给5条线程设置名称
            t1.setName("张飞");
            t2.setName("关羽");
            t3.setName("赵云");
            t4.setName("刘备");
            t5.setName("诸葛亮");
    
            // 启动所有线程
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
        }
    }
    
  • 运行结果

    张飞抢到了54.28999999999777元
    诸葛亮抢到了27.300000000001468元
    关羽抢到了18.410000000000764元
    刘备没有抢到红包~
    赵云没有抢到红包~
    
    Process finished with exit code 0
    
    

实现(四舍五入后,并保留2位小数)
  • 自定义抢红包的线程类

    package com.app.demo41_multithreading_test.test4;
    
    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.util.ArrayList;
    import java.util.Random;
    
    /*
        自定义抢红包的线程类
     */
    public class MyThreadPlus extends Thread {
        // 共享数据:100元、3个红包
        static BigDecimal money = BigDecimal.valueOf(100);
        static int count = 3;
    
        // 定义最小红包的金额:0.01元
        static final BigDecimal MIN_MONEY = BigDecimal.valueOf(0.01);
    
        // 重写run方法
        @Override
        public void run() {
            //1.循环:在当前业务中用不上,因为每个人只能抢一次红包
            //2.同步代码块
            synchronized (MyThreadPlus.class) {
                //3.判断共享数据(已经到末尾)
                if (count == 0) { // 3个红包已全部抢完
                    System.out.println(getName() + "没有抢到红包~");
                } else {
                    //4.判断共享数据(没有到末尾)
                    // 定义变量,用于记录抢到的红包金额
                    BigDecimal prize = BigDecimal.valueOf(0.0);
                    // 判断目前抢的是否为最后一个红包
                    if (count == 1) {
                        // 是,则直接将剩余的金额给出去
                        prize = money;
                    } else {
                        // 否,则随机99.98以内的数额给出去
                        // 创建随机数对象
                        Random r = new Random();
                        // 分析:因为如果随机100以内的数额,有可能会直接将100给随机出去了,这样就不符合需求了
                        // 所以要留两个0.01元做最小金额
    
                        // 计算:100 - 2*0.01 == 99.98
                        // subtract(): 减法;multiply(): 乘法;
                        // doubleValue(): 将BigDecimal类型转换成double类型
                        // BigDecimal.valueOf(): 将double类型转换成BigDecimal类型
                        double bound = (money.subtract(BigDecimal.valueOf(count - 1).multiply(MIN_MONEY))).doubleValue();
                        double i = MIN_MONEY.doubleValue();
    
                        // 随机一个99.98元以内的红包
                        // 用于存储i~bound(0.01~99.98)内的所有红包金额
                        ArrayList<Double> list = new ArrayList<>();
                        for (double j = i; j <= bound; j+=0.01) {
                            list.add(j);
                        }
                        // 随机一个集合长度内的索引
                        int index = r.nextInt(list.size());
                        // 根据这个随机索引,从红包集合list中随机一个红包金额出来
                        prize = BigDecimal.valueOf(list.get(index));
                    }
                    // 设置抢到的红包,小数点保留两位小数,四舍五入
                    prize = prize.setScale(2, RoundingMode.HALF_UP);
                    // 计算总金额剩余多少?
                    // 剩余金额 = 总金额 - 随机红包金额
                    money = money.subtract(prize);
                    // 红包的个数-1
                    count--;
                    // 输出提示
                    System.out.println(getName() + "抢到了" + prize + "元");
                }
            }
        }
    }
    
  • 测试类

    package com.app.demo41_multithreading_test.test4;
    
    public class TestPlus {
        public static void main(String[] args) {
            /*
                多线程综合练习:抢红包
                   需求:
                     假设:100元,分成3个红包,现在有5个人去抢。
                     要求:红包是共享数据;5个人是5条线程。
                     打印结果如下:
                      XXX抢到了XXX元
                      XXX抢到了XXX元
                      XXX抢到了XXX元
                      XXX没抢到
                      XXX没抢到
             */
            // 创建5条线程
            MyThreadPlus t1 = new MyThreadPlus();
            MyThreadPlus t2 = new MyThreadPlus();
            MyThreadPlus t3 = new MyThreadPlus();
            MyThreadPlus t4 = new MyThreadPlus();
            MyThreadPlus t5 = new MyThreadPlus();
    
            // 给5条线程设置名称
            t1.setName("张飞");
            t2.setName("关羽");
            t3.setName("赵云");
            t4.setName("刘备");
            t5.setName("诸葛亮");
    
            // 启动所有线程
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
        }
    }
    
  • 运行结果

    张飞抢到了90.07元
    诸葛亮抢到了6.99元
    赵云抢到了2.94元
    刘备没有抢到红包~
    关羽没有抢到红包~
    
    Process finished with exit code 0
    
    


五、抽奖箱抽奖

需求
  • 有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为{10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700}

  • 要求:创建两个抽奖箱(线程),设置线程名称分别为:抽奖箱1、抽奖箱2

  • 随机从抽奖池中获取奖项元素并打印出来(每次抽出一个奖项就随机打印一个):

    抽奖箱1 又产生了一个10元大奖
    抽奖箱1 又产生了一个100元大奖
    抽奖箱1 又产生了一个200元大奖
    抽奖箱1 又产生了一个800元大奖
    抽奖箱2 又产生了一个700元大奖
    ......
    

实现
  • 自定义抽奖箱抽奖的线程类

    package com.app.demo41_multithreading_test.test5;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    /*
        自定义抽奖箱抽奖的线程类
     */
    public class MyThread extends Thread{
        // 共享数据:奖池(用集合比较方便)
        ArrayList<Integer> list; // 不做初始化,当创建线程对象的时候传进来即可
    
        // 线程类有参构造方法
        public MyThread(ArrayList<Integer> list) {
            this.list = list;
        }
    
        // 重写run方法
    
        @Override
        public void run() {
            // 1.循环
            while (true) {
                // 2.同步代码块
                synchronized (MyThread.class) {
                    // 3.判断:共享数据已经到末尾
                    if (list.size() == 0) {
                        // 如果集合中的大小为0,说明所有奖已经全部抽完
                        break;
                    }else {
                        // 4.判断:共享数据没有到末尾
                        // 如果集合中的大小不为0,开始抽奖
                        // 将奖池中的奖金全部打乱顺序
                        Collections.shuffle(list);
                        // 第一个就是随机的奖金,并将抽到的奖金从奖池中删除
                        Integer prize = list.remove(0);
                        System.out.println(getName() + " 又产生了一个" + prize + "元大奖~");
                    }
                }
                // 为了能让两个线程平均的抽到奖,适当的让线程睡眠10毫秒
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
  • 测试类

    package com.app.demo41_multithreading_test.test5;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class Test {
        public static void main(String[] args) {
            /*
                多线程综合练习:抽奖箱抽奖
                  需求:
                    有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为{10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700}
                  要求:创建两个抽奖箱(线程),设置线程名称分别为:抽奖箱1、抽奖箱2
                    随机从抽奖池中获取奖项元素并打印出来(每次抽出一个奖项就随机打印一个):
                    抽奖箱1 又产生了一个10元大奖
                    抽奖箱1 又产生了一个100元大奖
                    抽奖箱1 又产生了一个200元大奖
                    抽奖箱1 又产生了一个800元大奖
                    抽奖箱2 又产生了一个700元大奖
                    ...
             */
            // 创建奖池
            ArrayList<Integer> list = new ArrayList<>();
            // 初始化奖池数据
            Collections.addAll(list, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);
    
            // 创建两个线程对象
            MyThread t1 = new MyThread(list);
            MyThread t2 = new MyThread(list);
            
            // 给线程对象设置名称
            t1.setName("抽奖箱1");
            t2.setName("抽奖箱2");
    
            // 启动所有线程对象
            t1.start();
            t2.start();
        }
    }
    
  • 运行结果

    抽奖箱1 又产生了一个5元大奖~
    抽奖箱2 又产生了一个50元大奖~
    抽奖箱1 又产生了一个100元大奖~
    抽奖箱2 又产生了一个800元大奖~
    抽奖箱1 又产生了一个700元大奖~
    抽奖箱2 又产生了一个2元大奖~
    抽奖箱2 又产生了一个20元大奖~
    抽奖箱1 又产生了一个300元大奖~
    抽奖箱2 又产生了一个80元大奖~
    抽奖箱1 又产生了一个10元大奖~
    抽奖箱2 又产生了一个500元大奖~
    抽奖箱1 又产生了一个200元大奖~
    
    Process finished with exit code 0
    
    


六、统计并求最大值

需求

在上一个抽奖练习的基础上继续完成如下需求:

  • 每次抽奖的过程中,不打印,抽完时一次性打印(随机):

    在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元。
    在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元。
    
  • 以上打印效果只是数据模拟,实际代码运行的效果会有差异


实现(方式1:不推荐)
package com.app.demo41_multithreading_test.test5case1;

import java.util.ArrayList;
import java.util.Collections;

/*
    自定义抽奖箱抽奖的线程类
 */
public class MyThread extends Thread{
    // 共享数据:奖池(用集合比较方便)
    ArrayList<Integer> list; // 不做初始化,当创建线程对象的时候传进来即可

    // 线程类有参构造方法
    public MyThread(ArrayList<Integer> list) {
        this.list = list;
    }

    // 定义集合,用于存储抽中的奖项
    static ArrayList<Integer> prizeList1 = new ArrayList<>();
    static ArrayList<Integer> prizeList2 = new ArrayList<>();

    // 重写run方法
    @Override
    public void run() {
        // 定义计数器
        int count1 = 0;
        int count2 = 0;

        // 1.循环
        while (true) {
            // 2.同步代码块
            synchronized (MyThread.class) {
                // 3.判断:共享数据已经到末尾
                if (list.size() == 0) {
                    // 如果集合中的大小为0,说明所有奖已经全部抽完(抽奖结束)
                    // 判断是哪个抽奖箱
                    if ("抽奖箱1".equals(getName())) {
                        show(getSum(prizeList1), getMax(prizeList1), count1, prizeList1);
                    }else {
                        show(getSum(prizeList2), getMax(prizeList2), count2, prizeList2);
                    }
                    break;
                }else {
                    // 4.判断:共享数据没有到末尾
                    // 如果集合中的大小不为0,开始抽奖
                    // 将奖池中的奖金全部打乱顺序
                    Collections.shuffle(list);
                    // 第一个就是随机的奖金,并将抽到的奖金从奖池中删除
                    int prize = list.remove(0);
                    // 判断是哪个抽奖箱
                    if ("抽奖箱1".equals(getName())) {
                        prizeList1.add(prize);
                        // 每抽中一个奖项,计数器变量值+1
                        count1++;
                    }else {
                        prizeList2.add(prize);
                        count2++;
                    }
                }
            }
            // 为了能让两个线程平均的抽到奖,适当的让线程睡眠10毫秒
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 显示抽奖结果
    public void show(int sum, int max, int count, ArrayList<Integer> list) {
        // 如果集合中的大小为0,说明所有奖已经全部抽完(抽奖结束)
        System.out.print("在此次抽奖过程中," + getName() + "总共产生了" + count + "个奖项,分别为:");
        // 遍历奖项集合,用于展示
        for (int i = 0; i < list.size(); i++) {
            System.out.print(i == list.size() - 1 ? list.get(i) + "最高奖项为" + max + "元,总计额为" + sum + "元。\n" : list.get(i) + ", ");
        }
    }

    // 求最大值
    public int getMax(ArrayList<Integer> list) {
        // 假设奖项集合中的首元素为最大值
        int max = list.get(0);
        // 求和、求最大值
        for (int prize : list) {
            // 依次用max变量中的值 与 每一个奖项进行比较
            if (prize > max) {
                // 如果当前这个奖项 大于 max变量中的值,则记录当前这个奖项prize为最大值
                max = prize;
            }
        }
        return max;
    }

    // 求和
    public int getSum(ArrayList<Integer> list) {
        // 定义求和变量
        int sum = 0;
        // 求和、求最大值
        for (int prize : list) {
            // 依次将每一个奖项数据进行累加
            sum += prize;
        }
        return sum;
    }
}
package com.app.demo41_multithreading_test.test5case1;

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    public static void main(String[] args) {
        /*
            多线程综合练习:统计并求最大值
              需求:
                在上一个抽奖练习的基础上继续完成如下需求:
                每次抽奖的过程中,不打印,抽完时一次性打印(随机):
                    在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元。
                    在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元。
                    以上打印效果只是数据模拟,实际代码运行的效果会有差异。
         */
        // 创建奖池
        ArrayList<Integer> list = new ArrayList<>();
        // 初始化奖池数据
        Collections.addAll(list, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);

        // 创建两个线程对象
        MyThread t1 = new MyThread(list);
        MyThread t2 = new MyThread(list);
        
        // 给线程对象设置名称
        t1.setName("抽奖箱1");
        t2.setName("抽奖箱2");

        // 启动所有线程对象
        t1.start();
        t2.start();
    }
}
在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:700, 80, 800, 100, 10, 300最高奖项为800元,总计额为1990元。
在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:2, 20, 50, 5, 500, 200最高奖项为500元,总计额为777元。

Process finished with exit code 0


实现(方式2:推荐)
package com.app.demo41_multithreading_test.test5case2;

import java.util.ArrayList;
import java.util.Collections;

/*
    自定义抽奖箱抽奖的线程类
 */
public class MyThread extends Thread {
    // 共享数据:奖池(用集合比较方便)
    ArrayList<Integer> list; // 不做初始化,当创建线程对象的时候传进来即可

    // 线程类有参构造方法
    public MyThread(ArrayList<Integer> list) {
        this.list = list;
    }

    // 重写run方法
    @Override
    public void run() {
        // 定义集合,用于存储奖项数据
        ArrayList<Integer> prizeList = new ArrayList<>();
        // 定义计数器
        int count = 0;

        // 1.循环
        while (true) {
            // 2.同步代码块
            synchronized (MyThread.class) {
                // 3.判断:共享数据已经到末尾
                if (list.size() == 0) {
                    // 如果集合中的大小为0,说明所有奖已经全部抽完(抽奖结束)
                    show(getSum(prizeList), getMax(prizeList), count, prizeList);
                    break;
                } else {
                    // 4.判断:共享数据没有到末尾
                    // 如果集合中的大小不为0,开始抽奖
                    // 将奖池中的奖金全部打乱顺序
                    Collections.shuffle(list);
                    // 第一个就是随机的奖金,并将抽到的奖金从奖池中删除
                    int prize = list.remove(0);
                    prizeList.add(prize);
                    // 每抽中一个奖项,计数器变量值+1
                    count++;
                }
            }
            // 为了能让两个线程平均的抽到奖,适当的让线程睡眠10毫秒
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 显示抽奖结果
    public void show(int sum, int max, int count, ArrayList<Integer> list) {
        // 如果集合中的大小为0,说明所有奖已经全部抽完(抽奖结束)
        System.out.print("在此次抽奖过程中," + getName() + "总共产生了" + count + "个奖项,分别为:");
        // 遍历奖项集合,用于展示
        for (int i = 0; i < list.size(); i++) {
            System.out.print(i == list.size() - 1 ? list.get(i) + "最高奖项为" + max + "元,总计额为" + sum + "元。\n" : list.get(i) + ", ");
        }
    }

    // 求最大值
    public int getMax(ArrayList<Integer> list) {
        // 假设奖项集合中的首元素为最大值
        int max = list.get(0);
        // 求和、求最大值
        for (int prize : list) {
            // 依次用max变量中的值 与 每一个奖项进行比较
            if (prize > max) {
                // 如果当前这个奖项 大于 max变量中的值,则记录当前这个奖项prize为最大值
                max = prize;
            }
        }
        return max;
    }

    // 求和
    public int getSum(ArrayList<Integer> list) {
        // 定义求和变量
        int sum = 0;
        // 求和、求最大值
        for (int prize : list) {
            // 依次将每一个奖项数据进行累加
            sum += prize;
        }
        return sum;
    }
}
package com.app.demo41_multithreading_test.test5case2;

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    public static void main(String[] args) {
        /*
            多线程综合练习:统计并求最大值
              需求:
                在上一个抽奖练习的基础上继续完成如下需求:
                每次抽奖的过程中,不打印,抽完时一次性打印(随机):
                    在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元。
                    在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元。
                    以上打印效果只是数据模拟,实际代码运行的效果会有差异。
         */
        // 创建奖池
        ArrayList<Integer> list = new ArrayList<>();
        // 初始化奖池数据
        Collections.addAll(list, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);

        // 创建线程对象
        MyThread t1 = new MyThread(list);
        MyThread t2 = new MyThread(list);
        MyThread t3 = new MyThread(list);

        // 给线程对象设置名称
        t1.setName("抽奖箱1");
        t2.setName("抽奖箱2");
        t3.setName("抽奖箱3");

        // 启动所有线程对象
        t1.start();
        t2.start();
        t3.start();
    }
}
在此次抽奖过程中,抽奖箱2总共产生了3个奖项,分别为:50, 2, 80最高奖项为80元,总计额为132元。
在此次抽奖过程中,抽奖箱3总共产生了4个奖项,分别为:20, 5, 500, 200最高奖项为500元,总计额为725元。
在此次抽奖过程中,抽奖箱1总共产生了5个奖项,分别为:100, 10, 800, 700, 300最高奖项为800元,总计额为1910元。

Process finished with exit code 0



七、多线程之间的比较

需求

在上一个统计并求最大值练习的基础上继续完成如下需求:

在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元。
在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元。
在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元。
  • 以上打印效果只是数据模拟,实际代码运行的效果会有差异

实现
  • 使用第三种方式自定义抽奖箱抽奖的线程类

    package com.app.demo41_multithreading_test.test6;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.concurrent.Callable;
    
    /*
        使用第三种方式自定义抽奖箱抽奖的线程类
     */
    public class MyCallable implements Callable<Integer> {
        // 共享数据:奖池(用集合比较方便)
        ArrayList<Integer> list; // 不做初始化,当创建线程对象的时候传进来即可
    
        // 线程类有参构造方法
        public MyCallable(ArrayList<Integer> list) {
            this.list = list;
        }
    
        // 显示抽奖结果
        public void show(int sum, int max, int count, ArrayList<Integer> list) {
            // 如果集合中的大小为0,说明所有奖已经全部抽完(抽奖结束)
            System.out.print("在此次抽奖过程中," + Thread.currentThread().getName() + "总共产生了" + count + "个奖项,分别为:");
            // 遍历奖项集合,用于展示
            for (int i = 0; i < list.size(); i++) {
                System.out.print(i == list.size() - 1 ? list.get(i) + "最高奖项为" + max + "元,总计额为" + sum + "元。\n" : list.get(i) + ", ");
            }
        }
    
        // 求最大值
        public int getMax(ArrayList<Integer> list) {
            // 假设奖项集合中的首元素为最大值
            int max = list.get(0);
            // 求和、求最大值
            for (int prize : list) {
                // 依次用max变量中的值 与 每一个奖项进行比较
                if (prize > max) {
                    // 如果当前这个奖项 大于 max变量中的值,则记录当前这个奖项prize为最大值
                    max = prize;
                }
            }
            return max;
        }
    
        // 求和
        public int getSum(ArrayList<Integer> list) {
            // 定义求和变量
            int sum = 0;
            // 求和、求最大值
            for (int prize : list) {
                // 依次将每一个奖项数据进行累加
                sum += prize;
            }
            return sum;
        }
    
        // 重写call方法
        @Override
        public Integer call() throws Exception {
            // 定义集合,用于存储奖项数据
            ArrayList<Integer> prizeList = new ArrayList<>();
            // 定义计数器
            int count = 0;
    
            // 1.循环
            while (true) {
                // 2.同步代码块
                synchronized (MyCallable.class) {
                    // 3.判断:共享数据已经到末尾
                    if (list.size() == 0) {
                        // 如果集合中的大小为0,说明所有奖已经全部抽完(抽奖结束)
                        // 展示抽奖箱抽奖结果
                        show(getSum(prizeList), Collections.max(prizeList), count, prizeList);
                        break;
                    } else {
                        // 4.判断:共享数据没有到末尾
                        // 如果集合中的大小不为0,开始抽奖
                        // 将奖池中的奖金全部打乱顺序
                        Collections.shuffle(list);
                        // 第一个就是随机的奖金,并将抽到的奖金从奖池中删除
                        int prize = list.remove(0);
                        prizeList.add(prize);
                        // 每抽中一个奖项,计数器变量值+1
                        count++;
                    }
                }
                // 为了能让两个线程平均的抽到奖,适当的让线程睡眠10毫秒
                Thread.sleep(10);
            }
            // 如果该线程没有抽到奖
            if (prizeList.size() == 0) {
                // 则返回null
                return null;
            }else {
                // 如果线程抽到奖,则返回该线程的最大值
                return Collections.max(prizeList);
            }
        }
    }
    
  • 测试类

    package com.app.demo41_multithreading_test.test6;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class Test {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            /*
                多线程综合练习:多线程之间的比较
                   需求:
                     在上一个统计并求最大值练习的基础上继续完成如下需求:
                        在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元。
                        在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元。
                        在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元。
                        以上打印效果只是数据模拟,实际代码运行的效果会有差异!!!
             */
            // 创建奖池
            ArrayList<Integer> list = new ArrayList<>();
            // 初始化奖池数据
            Collections.addAll(list, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);
    
            // 创建多线程要运行的参数对象
            MyCallable mc = new MyCallable(list);
    
            // 创建多线程运行结果的管理者对象
            FutureTask<Integer> ft1 = new FutureTask<>(mc);
            FutureTask<Integer> ft2 = new FutureTask<>(mc);
    
            // 创建线程对象
            Thread t1 = new Thread(ft1);
            Thread t2 = new Thread(ft2);
    
            // 给线程对象设置名称
            t1.setName("抽奖箱1");
            t2.setName("抽奖箱2");
    
            // 启动所有线程对象
            t1.start();
            t2.start();
    
            // 获取线程的
            Integer max1 = ft1.get();
            Integer max2 = ft2.get();
    
            // 线程之间的比较
            if (max1 > max2) {
                System.out.println("在此次抽奖过程中," + t1.getName() + "中产生了最大奖项,该奖项金额为" + max1 + "元。");
            }else {
                System.out.println("在此次抽奖过程中," + t2.getName() + "中产生了最大奖项,该奖项金额为" + max2 + "元。");
            }
        }
    }
    
  • 运行结果

    在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:50, 200, 20, 500, 2, 700最高奖项为700元,总计额为1472元。
    在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5, 100, 800, 80, 10, 300最高奖项为800元,总计额为1295元。
    在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元。
    
    Process finished with exit code 0
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值