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


相关文章
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
23 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
20 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
34 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
38 1
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
41 1
|
2月前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
28 1
|
21天前
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
55 0
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
51 1
C++ 多线程之初识多线程
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
62 6