💁 个人主页:黄小黄的博客主页
❤️ 支持我:👍 点赞 🌷 收藏 🤘关注
🎏 格言:All miracles start from sometime somewhere, make it right now.
本文来自专栏:JavaSE从入门到精通
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方法,是用于启动线程的。
判断题及简答题整理参考博主hl6621的文章,更多练习见文章:
java–多线程练习题
写在最后
🌟以上便是本文的全部内容啦,后续内容将会持续免费更新,如果文章对你有所帮助,麻烦动动小手点个赞 + 关注,非常感谢 ❤️ ❤️ ❤️ !
如果有问题,欢迎私信或者评论区!
共勉:“你间歇性的努力和蒙混过日子,都是对之前努力的清零。”