习题13_多线程

1.自己利用线程的第一种实现方式,实现如下功能(多线程引例):
a. 程序不停地在屏幕上输出一句问候的语句(比如“你好”)(时间间隔一定要大些比如3s(或大于3s),因为在控制台窗口,输入和输出不能同时发生,我们只能在两次输出“你好”的间隙,从键盘输入数据,才能保证键盘输入被正确接收)。
b.同时,当我通过键盘输入固定响应的时候,程序停止向屏幕输出问候的语句。

代码:

public class MultiThread {

 static boolean flag = true;

 public static void main(String[] args) throws InterruptedException {
   sayHelloRecycling();
   waitToStop();
   System.out.println("main end");
 }

 /**
  *  在子线程中,接收键盘输入,并根据键盘输入
  *  ,决定是否终止,在屏幕上输出问候语句
  */
 private static void waitToStop() {
   new Thread() {
     @Override
     public void run() {
       Scanner scanner = new Scanner(System.in);
       while (true) {
         String s = scanner.nextLine();
         if ("gun".equals(s)) {
           flag = false;
           scanner.close();
           break; //如果输入了gun,就终止程序的运行
         }
       }
     }
   }.start();
 }

 /**
  *  在子线程中,不停的在屏幕上输出问候语句,
  *  直到,循环的控制变量flag的值被改为false
  */
 private static void sayHelloRecycling() throws InterruptedException {
   new Thread() {
     @Override
     public void run() {
       while (flag) {
         System.out.println("哈哈, 你好!");

         try {
           Thread.sleep(3000);
         } catch (InterruptedException e) {
           e.printStackTrace();
         }
       }
     }
   }.start();
 }
}

运行结果:

在这里插入图片描述

2.阅读如下代码:
1)回答输出的是什么内容
输出的是:Thread匿名子类的run方法
2)为什么
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(“Runnable匿名子类的run方法”);
}
}) {
@Override
public void run() {
System.out.println(“Thread匿名子类的run方法”);
}
}.start();

解答:

原因如下:

  1. 首先,我们创建了一个Thread的匿名内部类对象

  2. 该匿名Thread子类对象接收一个Ruannable接口子类的匿名内部类对象

  3. 在Thread子类的匿名内部类定义中,我们覆盖了父类(Thread)的run方法

  4. 接着我们在Thread的匿名内部类对象上调用了start()方法,启动该Thread匿名内部类对象所表示的子线程

  5. 在该子线程(即Thread的匿名内部类对象)上调用start()方法,start()方法会调用Thread类的run()方法,但这个run方法在Thread的匿名子类定义中被子类覆盖了。所以实际在子线程中执行并非是Thread类的run()方法,而是Thread的匿名子类中定义的run()方法(即多态效果)

3.用多线程代码来模拟,迅雷用3个线程下载100M资源的的过程。
每个线程每次,一次下载1兆(M)资源,直到下载完毕,即剩余的待下载资源大小为0 (用一个整数表示资源大小,每次个线程每次下载多少兆(M), 剩余待下载资源就减少多少兆(M),考虑多线程的数据安全问题)。

代码:

package xxx;

public class ThreadSafe {
    public static void main(String[] args) {
        Download download = new Download();
        new Thread(download, "线程1").start();
        new Thread(download, "线程2").start();
        new Thread(download, "线程3").start();
    }
}
class Download implements Runnable{

    int resources = 50;

    private Object lock = new Object();

    @Override
    public void run() {
        while (resources > 0){

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lock){
                if(resources > 0){
                    System.out.println(Thread.currentThread().getName() +
                            "下载了1M资源,未下载资源还有" +  --resources +"M");
                    if(resources == 0){
                        System.out.println("资源下载完毕!");
                    }
                }
            }

        }
    }
}

运行结果:

在这里插入图片描述

3.创建2个线程,打印从0到99这100个数字,要求线程交叉顺序打印。(要求利用线程间通信api实现)
比如:
线程1: 0
线程2: 1
线程1: 2
线程2: 3
线程1: 4
线程2: 5

代码:

//参考答案
public class Work2 {
    /**
     *
     * 既然是两个线程交替打印,那就是一个线程打印奇数,一个线程打印偶数
     * 奇数线程打印奇数,然后让被打印数字增1
     * 偶数线程打印偶数,然后让被打印数字增1
     * 知道打印到题目中数字的上界99
     */
    public static void main(String[] args) {

        //创建PrintNumber对象,该对象记录了被打印数字,同时,创建该对象时,指定打印数字的上界
        PrintNumber printNumber = new PrintNumber(99);
        OddTask oddTask = new OddTask(printNumber);
        EvenTask evenTask = new EvenTask(printNumber);

        new Thread(evenTask, "线程1").start();
        new Thread(oddTask, "线程2").start();
    }

}

/*
   定义一个类,封装被打印的数字
*/
class PrintNumber {
    private int i = 0;

    //指定被打印数字的上线,在题目中upBound的取值为99,只要打印到99即可
    private int upBound;

    public PrintNumber(int upBound) {
        this.upBound = upBound;
    }

    /*
        让被打印数字增1
     */
    public void add() {
        i++;
    }

    /*
        获取打印数字的上界
     */
    public int getUpBound() {
        return upBound;
    }

    /*
        获取被打印数字当前的值
     */
    public int value() {
        return i;
    }
}


/*
     打印奇数,和偶数线程的公共父类,打印的主要逻辑都在该类中
     子类只实现,具体的打印条件的判断即可,可以很大程度的复用代码
*/
abstract class PrintTask implements Runnable {

    /*
         奇数偶数线程共同打印的对象,该对象中包含被打印数字的值
     */
    protected PrintNumber number;

    public PrintTask(PrintNumber number) {
        this.number = number;
    }

    @Override
    public void run() {
        //如果还没打印到最大值,就接着循环打印
        while (number.value() <= number.getUpBound()) {

            synchronized (number) {
                if (number.value() > number.getUpBound() ) {
                    //如果超出打印数字的上界,就退出循环不打印
                    break;
                }

                if (!shouldPrint()) {
                    //如果不满足打印条件,当前线程就不打印
                    try {
                        number.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                } else {
                    // 满足当前线程的打印条件

                    //打印数字的值
                    System.out.println(Thread.currentThread().getName() + " i = " + number.value());
                    //使被打印数字的值增1
                    number.add();

                    if (number.value() > number.getUpBound()) {
                        //被打印的数字的值,已经超过了了上界,即打印完了
                        System.out.println("打印完毕");
                    }

                    // 通知其他等待的打印线程,继续打印
                    number.notifyAll();
                }
            }

        }
    }
    // 该方法由子类实现,判断当前线程是否该打印数字i的值
    protected abstract boolean shouldPrint();
}

/*
   打印奇数
*/
class OddTask extends PrintTask {

    public OddTask(PrintNumber number) {
        super(number);
    }

    /*
        打印奇数的线程,当number对象中,i的值为奇数时才打印
     */
    @Override
    protected boolean shouldPrint() {
        return number.value() % 2 != 0;
    }
}

/*
   打印偶数
*/
class EvenTask extends PrintTask {

    public EvenTask(PrintNumber number) {
        super(number);
    }

    /*
       打印偶数的线程,当number对象中,i的值为偶数时才打印
    */
    @Override
    protected boolean shouldPrint() {
        return number.value() % 2 == 0;
    }
}

运行结果:

在这里插入图片描述
#个人学习记录,如发现有错误之处,欢迎与我交流

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值