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

简介: 文章目录1 编程题1.1 打印数字,读取通知1.2 取钱问题2 判断题3 简答题

1 编程题

1.1 打印数字,读取通知

(1)在main方法中启动两个线程;

(2)在1个线程循环打印100以内的整数;

(3)直到第2个线程从键盘读取了“Q”命令。


🐦 思路解析:


需要使用一个线程取控制另外一个线程;

main线程启动两个线程,一个线程打印100以内整数,另一个线程可以终止打印整数线程,可以考虑采用通知的方式终止打印线程;

记打印数字线程为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;
            }
        }
    }
}

🐱 扩展:

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


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


1.2 取钱问题

(1)有2个用户分别从一个卡上取钱(总额:5200元);

(2)每次都取500块钱;

(3)当余额不足时,不能再取钱;

(4)不能出现超取现象


🐦 思路解析:


考虑采用线程的同步机制来解决取钱问题;

两个用户取钱分别记为t1与t2两个线程;

当这两个线程进行取钱操作前,需要获得锁;

锁被一个线程占用时,另一个线程进入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;
    }
}

🐰 结果如下:




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


答: 在该程序中,使用到的是this对象锁,该锁为非公平锁。

比如:t1、t2、t3三个线程都进行获取锁的操作,而每次都是t1获取到锁,就会导致t2、t3两个线程都被阻塞(Blocked),看起来就像没有执行一样。


2 判断题

如果线程死亡,它便不能运行。(T)

在Java中,高优先级的可运行线程会抢占低优先级线程。(T )

线程可以用yield方法使低优先级的线程运行。(F)

程序开发者必须创建一个线程去管理内存的分配。(T)

一个线程在调用它的start方法之前,该线程将一直处于出生期。( T)

当调用一个正在进行线程的stop( )方法时,该线程便会进入休眠状态。(F)

一个线程可以调用yield方法使其他线程有机会运行。(T)

多线程没有安全问题(F)

多线程安全问题的解决方案可以使用Lock提供的具体的锁对象操作(T)

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方法,是用于启动线程的。


相关文章
|
8月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
355 0
|
8月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
9月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
639 5
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
486 20
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
284 1
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
535 0
|
5月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
244 6
|
8月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
396 83
|
10月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
389 0

热门文章

最新文章