day12作业
day12作业
点击回到笔记 📒👈
一、选择题
1. Java语言中提供了一个( )线程,自动回收动态分配的内存
- A. 异步
- B. 消费者
- C. 守护
- D. 垃圾收集
答案
D GC垃圾回收机制
2. Java语言避免了大多数的( )是错误的。
- A. 数组下标越界
- B. 算术溢出
- C. 内存泄露
- D. 非法的方法参数
答案
C 内存泄露是程序运行过程中发生的
3. 有三种原因可以导致线程不能运行,它们是()
- A.等待
- B.阻塞
- C.休眠
- D.由于I/O操作而阻塞
答案
ACD
使用 Java 阻塞 I/O 模型读取数据,将会导致线程阻塞,线程将会进入休眠,从而让出 CPU 的执行权,直到数据读取完成。
一旦获取不到锁,线程状态将会变成 BLOCKED,等待获取锁。一旦有其他线程释放这把锁,线程成功抢到该锁,线程状态就将会从 BLOCKED 转变为 RUNNABLE 状态。
4.当()方法终止时,能使线程进入死亡状态。
- A.run
- B.setPrority
- C.sleep
- D.yield
答案
A
- run方法里面的逻辑就是程序
- setPrority 设置优先级
yield()方法的作用是放弃当前的CPU资源,让其他任务去占用CUP执行时间。类似sleep。 但是:
- sleep() 方法给其他线程运行机会时不考虑线程的优先级;
- yield() 方法只会给相同优先级或更高优先级的线程运行的机会
5.用()方法可以改变线程的优先级。
- A.run
- B.setPrority
- C.yield
- D.sleep
答案
B 不设置是有默认优先级
7.线程通过()方法可以休眠一段时间,然后恢复运行。
- A.run
- B.setPrority
- C.yield
- D.sleep
答案
D 单位为是毫秒
yield没有时间参数
8. 关于下列同步说法错误的是 ( )
- A. 同步代码块可以锁住指定代码,同步方法是锁住方法中所有代码
- B. 同步代码块可以指定锁对象,同步方法不能指定锁对象
- C. 对于非static方法,同步锁是this
- D. 对于static方法,同步锁是调用此方法的对象
答案
D 对于static方法,同步锁是字节码对象(唯一性)
二、今日方法:
1. 线程同步涉及到的关键字以及类名和方法名:
提示
- synchronized
- new ReentrantLock()
- lock
- unlock
2. 线程六种状态的名称:
提示

3. 线程工具类中,能创建不同线程池的方法名称:
提示

三、简答题:
1. 多线程是不是就是线程不安全?为什么会发生线程安全?
提示
- 多线程
- 共享资源
- 修改共享资源的操作
2. 有几种方式解决线程安全?需要注意什么?
提示
3种,锁不一样
- 锁具有唯一性
- 同步代码块,可以用this
- 同步方法,锁就是this
- 同步静态方法,锁就是字节码文件对象
- new ReentrantLock() 本身可以加锁和释放锁
3. 简述通过ThreadPoolExecutor创建线程池时,构造方法对应的7个参数分别表示什么意思?并说明常见的任务的拒绝策略都有哪些?
提示


4. 为什么要使用线程池,线程池有什么优势?
提示

5. 简述线程池的工作流程?
提示

四、排错题:
排错题1:分析当执行如下程序的ThreadDemo01的main方法的时候,在控制台会输出什么内容?如果想一直输出"执行了======"应该对该程序如何改造【只允许对flag变量进行相关修改】?并且说明原因?

提示
主要考察:多线程中的可见性
可见性
可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。
可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。
在 Java 中 volatile、synchronized 都可以实现可见性。

原始代码
public class demo1 {
public static void main(String[] args) {
MyThread2 myAtomThread = new MyThread2();
myAtomThread.start();
while (true){
if (myAtomThread.isFlag()){
System.out.println("执行了");
}
}
}
}
class MyThread2 extends Thread{
private boolean flag =false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.flag=true;
System.out.println("flag="+flag);
}
}
排错题2:以下代码在控制台输出结果是什么?多运行几次试试?如果结果不是如你所愿,请试着猜一下原因~

提示
- 安全问题(超卖)
- 解决方案加锁

原始代码
public class demo {
public static void main(String[] args) {
MyAtomThread myAtomThread = new MyAtomThread();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(myAtomThread);
thread.start();
}
}
}
class MyAtomThread implements Runnable{
private int count=0;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
count++;
System.out.println("已经送了"+count+"个冰激凌");
}
}
}
五、代码题:
第一题:分析以下需求,并用代码实现
训练目标:
掌握java中多线程基本使用
需求描述: 有100份礼品,小红,小明两人同时发送,当剩下的礼品小于10份的时候则不再送出,利用多线程模拟该过程并将线程的名称打印出来。并最后在控制台分别打印小红,小明各自送出多少分礼物。
点击查看代码和解析
- 在该程序中,通过创建一个 GiftSending 类实现 Runnable 接口来实现多线程,
- 并使用 synchronized 关键字来保证多线程对礼品数量的访问是同步的。
- 在每个线程中,如果礼品数量大于0,则进行送礼操作,并记录该线程送出的礼品数量。
- 在礼品数量少于10时,停止送礼并在控制台输出小红、小明各自送出的礼品数量。
- 最后在 main 方法中创建两个线程,分别代表小红和小明,然后启动这两个线程即可。
public class GiftSending implements Runnable {
private static int giftCount = 100; // 礼品总数
private static int redCount = 0; // 小红送出礼品数量
private static int mingCount = 0; // 小明送出礼品数量
private String name; // 线程名称
public GiftSending(String name) {
this.name = name;
}
public void run() {
while (giftCount > 10) { // 礼品数量大于10,继续送礼
synchronized (GiftSending.class) { // 同步代码块,保证礼品数量正确
if (giftCount > 0) { // 还有礼品剩余
System.out.println(name + "送出了一份礼物,礼品总数:" + (--giftCount));
if (name.equals("小红")) {
redCount++; // 记录小红送出的礼品数量
} else {
mingCount++; // 记录小明送出的礼品数量
}
}
}
try {
Thread.sleep(100); // 暂停一段时间,模拟送礼过程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name + "共送出了" + (name.equals("小红") ? redCount : mingCount) + "份礼物");
}
public static void main(String[] args) {
Thread redThread = new Thread(new GiftSending("小红"));
Thread mingThread = new Thread(new GiftSending("小明"));
redThread.start();
mingThread.start();
}
}
第二题:分析以下需求,并用代码实现
训练目标:
掌握java中多线程基本使用
需求描述:
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”,“抽奖箱2”,随机从抽奖池中获取奖项元素并打印在控制台上,格式如下:
1.每次抽出一个奖项就打印一个(随机)
抽奖箱1 又产生了一个 10 元大奖
抽奖箱1 又产生了一个 100 元大奖
抽奖箱1 又产生了一个 200 元大奖
抽奖箱1 又产生了一个 800 元大奖
抽奖箱2 又产生了一个 700 元大奖
//.....
2.每次抽的过程中,不打印,抽完时一次性打印(随机)
在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元
在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元
3.每次抽的过程中,不打印,抽完时一次性打印(随机)
在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,20,100,500,2,300最高奖项为300元,总计额为932元
在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:5,50,200,800,80,700最高奖项为800元,总计额为1835元
在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元
以上打印效果只是数据模拟,实际代码运行的效果会有差异
点击查看代码
public class LotteryBox implements Runnable {
private String name; // 抽奖箱名称
private List<Integer> rewards = new ArrayList<>(); // 抽奖池奖项列表
private List<Integer> pickedRewards = new ArrayList<>(); // 已抽中奖项列表
private Random random = new Random(); // 随机数生成器
public LotteryBox(String name, List<Integer> rewards) {
this.name = name;
this.rewards = rewards;
}
@Override
public void run() {
int i = 0;
int totalRewards = 0;
int maxReward = 0;
// 模拟抽奖过程
while (rewards.size() > 0) {
int reward = rewards.remove(random.nextInt(rewards.size()));
i++;
pickedRewards.add(reward);
// 打印单次抽奖结果
System.out.println(this.name + " 又产生了一个 " + reward + " 元大奖");
//累计总的金额
totalRewards += reward;
//求出最大奖项
maxReward = Math.max(maxReward, reward);
// 模拟线程抽奖速度不同,让其中一个线程先休眠一段时间
// if (name.equals("抽奖箱1")) {
// try {
// Thread.sleep(random.nextInt(10));
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
}
// 打印一次抽奖结果汇总
System.out.println("在此次抽奖过程中," + this.name + "总共产生了" + i + "个奖项,分别为:" + pickedRewards +
",最高奖项为" + maxReward + "元,总计额为" + totalRewards + "元");
}
public static void main(String[] args) throws InterruptedException {
List<Integer> rewards = new ArrayList<>(); // 抽奖池奖项列表
rewards.add(10);
rewards.add(5);
rewards.add(20);
rewards.add(50);
rewards.add(100);
rewards.add(200);
rewards.add(500);
rewards.add(800);
rewards.add(2);
rewards.add(80);
rewards.add(300);
rewards.add(700);
// 创建两个抽奖箱线程并启动
LotteryBox box1 = new LotteryBox("抽奖箱1", rewards);
LotteryBox box2 = new LotteryBox("抽奖箱2", rewards);
Thread thread1 = new Thread(box1);
Thread thread2 = new Thread(box2);
thread1.start();
thread2.start();
// 等待两个线程执行完毕
thread1.join();
thread2.join();
// 主线程 打印抽奖结果中最大的奖项 使用steam流
Integer integer1 = box1.pickedRewards.stream().max((a, b) -> a - b).get();
Integer integer2 = box2.pickedRewards.stream().max((a, b) -> a - b).get();
int maxReward = Math.max(integer1, integer2);
if (box1.pickedRewards.contains(maxReward)) {
System.out.println("此次抽奖1中了最大将");
} else {
System.out.println("此次抽奖2中了最大将");
}
}}
选做题
第三题:分析以下需求,并用代码实现 🚀训练目标:
掌握java中多线程的线程通信基本使用
需求描述:
用两个线程玩猜数字游戏,第一个线程负责随机给出1~100之间的一个整数,第二个线程负责猜出这个数。 要求:
- 每当第二个线程给出自己的猜测后,第一个线程都会提示“猜小了”、“猜 大了”或“猜对了”。
- 猜数之前,要求第二个线程要等待第一个线程设置好 要猜测的数。
- 第一个线程设置好猜测数之后,两个线程还要相互等待,其原则是:
- 第二个线程给出自己的猜测后,等待第一个线程给出的提示;
- 第一个 线程给出提示后,等待第二个线程给出猜测,如此进行,直到第二个线程给 出正确的猜测后,两个线程进入死亡状态。
点击查看代码
import java.util.Random;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class GuessNumberGame {
public static void main(String[] args) {
// 创建一个阻塞队列
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(1);
// 创建一个线程用来生成随机数并放入队列中
Thread generator = new Thread(new NumberGenerator(queue));
// 创建一个线程用来从队列中取出数字并进行猜测
Thread guesser = new Thread(new NumberGuesser(queue));
// 启动两个线程
generator.start();
guesser.start();
}
}
// 生成随机数的线程类
class NumberGenerator implements Runnable {
private BlockingQueue<Integer> queue;
public NumberGenerator(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
// 生成1~100之间的一个随机数
Random random = new Random();
int number = random.nextInt(100) + 1;
try {
// 将随机数放入队列中
queue.put(number);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 进行猜测的线程类
class NumberGuesser implements Runnable {
private BlockingQueue<Integer> queue;
public NumberGuesser(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
// 等待生成者生成数字
int number = queue.take();
System.out.println("生成的数字是:" + number);
// 循环猜数,直到猜对为止
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("请输入猜测的数字:");
int guess = scanner.nextInt();
if (guess == number) {
System.out.println("恭喜你,猜对了!");
break;
} else if (guess < number) {
System.out.println("猜小了!");
} else {
System.out.println("猜大了!");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}