day12作业

YangeIT大约 11 分钟

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. 线程六种状态的名称:

提示

1668070204768
1668070204768

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

提示


三、简答题:

1. 多线程是不是就是线程不安全?为什么会发生线程安全?

提示

  1. 多线程
  2. 共享资源
  3. 修改共享资源的操作

2. 有几种方式解决线程安全?需要注意什么?

提示

3种,锁不一样

  • 锁具有唯一性
  1. 同步代码块,可以用this
  2. 同步方法,锁就是this
  3. 同步静态方法,锁就是字节码文件对象
  4. new ReentrantLock() 本身可以加锁和释放锁

3. 简述通过ThreadPoolExecutor创建线程池时,构造方法对应的7个参数分别表示什么意思?并说明常见的任务的拒绝策略都有哪些?

提示

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

提示

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

提示


四、排错题:

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

image-20211222180935477
image-20211222180935477

提示

主要考察:多线程中的可见性

可见性

可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。

可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。

在 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:以下代码在控制台输出结果是什么?多运行几次试试?如果结果不是如你所愿,请试着猜一下原因~

image-20211222180935477
image-20211222180935477

提示

  1. 安全问题(超卖)
  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份的时候则不再送出,利用多线程模拟该过程并将线程的名称打印出来。并最后在控制台分别打印小红,小明各自送出多少分礼物。

点击查看代码和解析
  1. 在该程序中,通过创建一个 GiftSending 类实现 Runnable 接口来实现多线程,
  2. 并使用 synchronized 关键字来保证多线程对礼品数量的访问是同步的。
  3. 在每个线程中,如果礼品数量大于0,则进行送礼操作,并记录该线程送出的礼品数量。
  4. 在礼品数量少于10时,停止送礼并在控制台输出小红、小明各自送出的礼品数量。
  5. 最后在 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元,总计额为18353.每次抽的过程中,不打印,抽完时一次性打印(随机)
		在此次抽奖过程中,抽奖箱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之间的一个整数,第二个线程负责猜出这个数。 要求:

  1. 每当第二个线程给出自己的猜测后,第一个线程都会提示“猜小了”、“猜 大了”或“猜对了”。
  2. 猜数之前,要求第二个线程要等待第一个线程设置好 要猜测的数。
  3. 第一个线程设置好猜测数之后,两个线程还要相互等待,其原则是:
    • 第二个线程给出自己的猜测后,等待第一个线程给出的提示;
    • 第一个 线程给出提示后,等待第二个线程给出猜测,如此进行,直到第二个线程给 出正确的猜测后,两个线程进入死亡状态。
点击查看代码

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();
        }
    }
}