• 【JavaSE】多线程篇(五)线程专项练习题


    💁 个人主页:黄小黄的博客主页
    ❤️ 支持我:👍 点赞 🌷 收藏 🤘关注
    🎏 格言:All miracles start from sometime somewhere, make it right now.
    本文来自专栏:JavaSE从入门到精通
    在这里插入图片描述


    1 编程题

    1.1 打印数字,读取通知

    (1)在main方法中启动两个线程;
    (2)在1个线程循环打印100以内的整数;
    (3)直到第2个线程从键盘读取了“Q”命令。

    🐦 思路解析:

    1. 需要使用一个线程取控制另外一个线程;
    2. main线程启动两个线程,一个线程打印100以内整数,另一个线程可以终止打印整数线程,可以考虑采用通知的方式终止打印线程;
    3. 记打印数字线程为A,监听键盘线程为B。要想让线程B能够通知线程A,需要B线程持有A线程对象,通过改变A线程的某个参数,达到通知A线程终止的目的。

    在这里插入图片描述

    ⌨️ 参考代码:

    /**
     * @author 兴趣使然黄小黄
     * @version 1.0
     */
    public class ThreadTest01 {
        public static void main(String[] args) {
            PrintNum printNum = new PrintNum();
            Thread printThread = new Thread(printNum);
            Thread readThread = new Thread(new ReadKeyboard(printNum));
            printThread.start();
            readThread.start();
        }
    }
    
    /**
     * 打印数字的线程
     */
    class PrintNum implements Runnable{
        private boolean loop = true;
        @Override
        public void run() {
            while (loop) {
                System.out.println((int)(Math.random() * 100 + 1));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void setLoop(boolean loop) {
            this.loop = loop;
        }
    }
    
    /**
     * 读取键盘的线程
     */
    class ReadKeyboard implements Runnable{
        private PrintNum printNum;
        private Scanner scanner = new Scanner(System.in);
    
        /**
         * 利用构造器,传入PrintNum线程对象,保证对象为同一个
         * @param printNum
         */
        public ReadKeyboard(PrintNum printNum) {
            this.printNum = printNum;
        }
    
        @Override
        public void run() {
            //接收用户输入
            while (true){
                System.out.println("输入Q退出程序: ");
                char key = scanner.next().toUpperCase().charAt(0);
                if (key == 'Q'){
                    //通知PrintNum线程退出
                    printNum.setLoop(false);
                    break;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    🐱 扩展:

     也 可以考虑将loop设置为静态成员, 这样另一个线程就无需再传入一个打印线程的对象了,具体见下图:
    在这里插入图片描述

     同时,也可以采用守护线程的做法, 将打印数字作为读取Q线程的守护线程,当读取Q线程消亡时,作为守护线程的打印数字线程,也会停止,参考图如下:
    在这里插入图片描述

    1.2 取钱问题

    (1)有2个用户分别从一个卡上取钱(总额:5200元);
    (2)每次都取500块钱;
    (3)当余额不足时,不能再取钱;
    (4)不能出现超取现象

    🐦 思路解析:

    1. 考虑采用线程的同步机制来解决取钱问题;
    2. 两个用户取钱分别记为t1与t2两个线程;
    3. 当这两个线程进行取钱操作前,需要获得锁;
    4. 锁被一个线程占用时,另一个线程进入blocked阻塞状态,等待获得锁,才能进行取钱。

    在这里插入图片描述

    ⌨️ 参考代码:

    /**
     * @author 兴趣使然黄小黄
     * @version 1.0
     * 取钱问题
     */
    public class ThreadTest02 {
        public static void main(String[] args) {
            WithDrawMoney withDrawMoney = new WithDrawMoney();
            Thread thread1 = new Thread(withDrawMoney);
            Thread thread2 = new Thread(withDrawMoney);
            thread1.setName("路人甲");
            thread2.setName("路人乙");
            thread1.start();
            thread2.start();
        }
    }
    
    /**
     * 取钱的线程
     */
    class WithDrawMoney implements Runnable{
        private int money = 5200;
        private int draw = 500;
        @Override
        public void run() {
            synchronized (this){
                while (true){
                    //余额是否足够
                    if (money < draw){
                        System.out.println("余额不足... ...");
                        break;
                    }
                    //取钱
                    money -= draw;
                    System.out.println(Thread.currentThread().getName() + "取走了 " + draw + "元, 当前余额: " + money + "元");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    
        public void setDraw(int draw) {
            this.draw = draw;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    🐰 结果如下:

    在这里插入图片描述

    🐯 为什么都是路人甲,路人乙去哪里了?

    答: 在该程序中,使用到的是this对象锁,该锁为非公平锁。
    比如:t1、t2、t3三个线程都进行获取锁的操作,而每次都是t1获取到锁,就会导致t2、t3两个线程都被阻塞(Blocked),看起来就像没有执行一样。


    2 判断题

    1. 如果线程死亡,它便不能运行。(T)
    2. 在Java中,高优先级的可运行线程会抢占低优先级线程。(T )
    3. 线程可以用yield方法使低优先级的线程运行。(F)
    4. 程序开发者必须创建一个线程去管理内存的分配。(T)
    5. 一个线程在调用它的start方法之前,该线程将一直处于出生期。( T)
    6. 当调用一个正在进行线程的stop( )方法时,该线程便会进入休眠状态。(F)
    7. 一个线程可以调用yield方法使其他线程有机会运行。(T)
    8. 多线程没有安全问题(F)
    9. 多线程安全问题的解决方案可以使用Lock提供的具体的锁对象操作(T)
    10. Stop()方法是终止当前线程的一种状态(T)

    3 简答题

    1️⃣ 简述程序、进程和线程之间的关系?什么是多线程程序?

    答:程序: 程序就是一段代码,一组指令的集合,不能单独运行,需要将其加载到内存中,系统为他分配资源后才能执行,运行时就相当于一个进程。
    进程: 进程就是系统分配资源调用的一个独立单位。是程序的一次动态执行,从加载到执行到执行完毕是一个完整的过程,并且有自己的生命周期。
    多线程: 一个程序运行时(进程)产生了不止一个线程,执行的路径有多条,就叫多线程。

    2️⃣ 什么是线程调度?Java的线程调度采用什么策略?

    答:线程调度:
    对处于可运行状态的多个线程对象进行系统级的协调,防止多个线程争用有限资源而导致系统死机或者崩溃
    java的线程调度采用的策略:
    java 的调度策略是基于线程优先级的抢先式调度。意思就是,谁的优先级高那我就先给谁使用系统资源。

    3️⃣ 在Java中wait()和sleep()方法的不同?

    答:(1)wait方法是在Object类中,而sleep方法是Thread类中
    (2)sleep方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是它的监控状态依然保持,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。

    4️⃣ 如何在Java程序中实现多线程?

    答:1)定义一个类继承自Thread类,重写run方法,然后创建这个类的对象,然后通过对象调用start方法启动线程。
    2)定义一个类实现Runnable接口,重写run方法,然后创建一个这个类的子类对象,然后建Thread类的对象,将子类对象作为参数进行传递,然后通过start方法启动线程。
    3)线程池,使用ExecutorService、Callable、Future实现有返回结果的多线程。
    4)JDK5以后新增了一个Executors工厂类来产生线程池,利用工厂类调用newFixedThreadPool方法,创建一个线程池对象,然后用线程池对象调用submit方法,传入的参数是一个实现了Callable接口的子类,重写了里面的call方法,submit方法相当于start方法,是用于启动线程的。

    判断题及简答题整理参考博主hl6621的文章,更多练习见文章:
    java–多线程练习题


    写在最后

    🌟以上便是本文的全部内容啦,后续内容将会持续免费更新,如果文章对你有所帮助,麻烦动动小手点个赞 + 关注,非常感谢 ❤️ ❤️ ❤️ !
    如果有问题,欢迎私信或者评论区!
    在这里插入图片描述

    共勉:“你间歇性的努力和蒙混过日子,都是对之前努力的清零。”
    在这里插入图片描述

  • 相关阅读:
    数据结构与算法——线性查找法
    Java基于SSM的海淘商城系统
    【蓝桥杯真题练习】STEMA科技素养练习题库 答案版009 持续更新中~
    JVM的内存模型
    表单设计器附件的上传、显示和下载
    Spring Cloud Gateway官方文档学习
    前端周刊第三十期
    Linux 文本处理三剑客:grep、sed 和 awk
    隐马尔科夫模型的简单实现
    学会Kafka这一篇就够了
  • 原文地址:https://blog.csdn.net/m0_60353039/article/details/126736546