软件设计基础实践-Java高级特性 - 多线程基础(2)

  • 1、

    有三种原因可以导致线程不能运行,它们是( ABC)

    A、

    等待

    B、

    阻塞

    C、

    休眠

    D、

    挂起及由于I/O操作而阻塞

  • 2、

    Java语言中提供了一个( D)线程,自动回收动态分配的内存。

    A、

    异步

    B、

    消费者

    C、

    守护

    D、

    垃圾收集

  • 3、

    当( A)方法终止时,能使线程进入死亡状态

    A、

    run

    B、

    setPrority

    C、

    yield

    D、

    sleep

  • 4、

    用(B )方法可以改变线程的优先级。

    A、

    run

    B、

    setPrority

    C、

    yield

    D、

    sleep

  • 5、

    线程通过(D )方法可以休眠一段时间,然后恢复运行

    A、

    run

    B、

    setPrority

    C、

    yield

    D、

    sleep

  • 6、

    下列关于线程的说法正确的是( ABD)

    A、

    如果线程死亡,它便不能运行

    B、

    在Java中,高优先级的可运行线程会抢占低优先级线程

    C、

    线程可以用yield方法使低优先级的线程运行

    D、

    一个线程可以调用yield方法使其他线程有机会运行

第2关:常用函数(一)

  • 任务要求
  • 参考答案
  • 评论93

任务描述

本关任务:获取子线程执行的结果并输出。

相关知识

本关你需要掌握sleepjoin函数的用法。

sleep()函数

sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。

使用方式很简单在线程的内部使用Thread.sleep(millis)即可。

sleep()使当前线程进入停滞状态(阻塞当前线程),让出CPU的使用,目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;

sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。

join()函数

join函数的定义是指:等待线程终止

我们在运行线程的时候可能会遇到,在主线程中运行子线程,主线程需要获取子线程最终执行结果的情况。

但是有很多时候子线程进行了很多耗时的操作,主线程往往先于子线程结束,这个时候主线程就获取不到子线程的最终执行结果了。

使用join函数,可以解决这一问题。

我们通过两个示例来理解:

不使用join函数的版本:

 
  1. class MyThread extends Thread {
  2. private String name;
  3. public MyThread(String name) {
  4. this.name = name;
  5. }
  6. public void run() {
  7. System.out.println("子线程开始运行");
  8. for (int i = 0; i < 5; i++) {
  9. System.out.println("子线程" + name + "运行" + i);
  10. try {
  11. Thread.sleep(30);
  12. } catch (InterruptedException e) {
  13. e.printStackT\frace();
  14. }
  15. }
  16. System.out.println("子线程结束");
  17. }
  18. }
  19. public class Test {
  20. public static void main(String[] args) {
  21. Thread t = new MyThread("子线程A");
  22. t.start();
  23. for (int i = 0; i < 5; i++) {
  24. System.out.println("主线程执行" + i);
  25. }
  26. System.out.println("主线程结束");
  27. }
  28. }

输出结果(每次都不一样):

主线程执行0 主线程执行1 主线程执行2 主线程执行3 主线程执行4 主线程结束 子线程开始运行 子线程子线程A运行0 子线程子线程A运行1 子线程子线程A运行2 子线程子线程A运行3 子线程子线程A运行4 子线程结束

可以发现每次运行,主线程都是先于子线程结束的。

使用join函数:

 
  1. package step1;
  2. class MyThread extends Thread {
  3. private String name;
  4. public MyThread(String name) {
  5. this.name = name;
  6. }
  7. public void run() {
  8. System.out.println("子线程开始运行");
  9. for (int i = 0; i < 5; i++) {
  10. System.out.println("子线程" + name + "运行" + i);
  11. try {
  12. Thread.sleep(30);
  13. } catch (InterruptedException e) {
  14. e.printStackT\frace();
  15. }
  16. }
  17. System.out.println("子线程结束");
  18. }
  19. }
  20. public class Test {
  21. public static void main(String[] args) {
  22. Thread t = new MyThread("子线程A");
  23. t.start();
  24. for (int i = 0; i < 5; i++) {
  25. System.out.println("主线程执行" + i);
  26. }
  27. try {
  28. t.join();
  29. } catch (Exception e) {
  30. e.printStackT\frace();
  31. }
  32. System.out.println("主线程结束");
  33. }
  34. }

输出结果:

主线程执行0 主线程执行1 主线程执行2 主线程执行3 子线程开始运行 主线程执行4 子线程子线程A运行0 子线程子线程A运行1 子线程子线程A运行2 子线程子线程A运行3 子线程子线程A运行4 子线程结束 主线程结束

可以发现无论运行多少次,主线程都会等待子线程结束之后在结束。

编程要求

请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,具体任务如下:

  • 创建自定义线程,实现求第num项斐波那契数列的值num0开始,并且在main函数中获取子线程最终计算的结果。
测试说明

补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。

输入:5 输出:子线程计算结果为:5

输入:8 输出:子线程计算结果为:21

输入:10 输出:子线程计算结果为:55


开始你的任务吧,祝你成功!

package step2;
import java.util.Scanner;
public class Task {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		//请在此添加实现代码
     /********** Begin **********/
        Thread t = new MyThread("子线程",num);
		t.start();
    /********** End **********/
	}
}
	//请在此添加实现代码
   /********** Begin **********/
class MyThread extends Thread{
	private int num;
	private String name;
	public MyThread(String name,int num){
		this.num=num;
		this.name=name;
	}
	public void run(){
		int[] arr = new int[2];
        arr[0] = 1;
        arr[1] = 1;
        for (int i = 2; i < num; i++) {
            int tmp = arr[1];
            arr[1] = arr[0] + arr[1];
            arr[0] = tmp;
        }
        System.out.println("子线程计算结果为:"+arr[1]);
	}
}
   /********** End **********/

第3关:常用函数(二)

  • 任务要求
  • 参考答案
  • 评论93

任务描述

本关任务:使用三个线程交替打印5EDU

相关知识

为了完成本关任务你需要掌握:

1.yield函数的用法;

2.wait函数的用法;

3.其他常用函数的使用。

yield 函数

yield函数可以理解为“让步”,它的作用是:暂停当前正在执行的线程对象,并执行其他线程。

yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

示例:

 
  1. public class Task {
  2. public static void main(String[] args) {
  3. System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
  4. MyThread mTh1=new MyThread("A");
  5. MyThread mTh2=new MyThread("B");
  6. mTh1.start();
  7. mTh2.start();
  8. System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
  9. }
  10. }
  11. class MyThread extends Thread {
  12. private String name;
  13. public MyThread(String name) {
  14. this.name = name;
  15. }
  16. public void run() {
  17. for (int i = 0; i < 50; i++) {
  18. System.out.println(name + "线程" + "执行" + i);
  19. if(i == 10){
  20. this.yield();
  21. }
  22. }
  23. }
  24. }

运行结果有两种情况:

第一种情况:A(线程)当执行到10时会让掉CPU时间,这时B(线程)抢到CPU时间并执行。

第二种情况:B(线程)当执行到10时会让掉CPU时间,这时A(线程)抢到CPU时间并执行。

我们还可以考虑在其中加入setPriority函数改变线程优先级从而改变线程的执行顺序。

wait 函数

要弄明白wait函数我们首先需要了解线程锁的概念。 线程锁:其实就像我们日常生活中的锁,如果一个房子上了锁,别人就进不去,在Java中也类似,如果一段代码取得了锁,那么其他地方就不能运行这段代码,只能等待锁的释放。

这里我们会涉及到两个函数,1.wait(),2.notify()。这两个函数都是Object类自带的函数。

在下面的例子中我们会使用synchronized(Obj)来实现线程同步,同步的概念后面会讲到,这里不理解没关系,不影响对于wait函数的理解。

从功能上来说:

  • wait()就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行;

  • 相应的notify()就是对对象锁的唤醒操作。

但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。

Thread.sleep()Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

纸上得来终觉浅,欲知此事须躬行,我们还是通过实例来学习这些函数的用法。

问题:建立两个线程,A线程打印5A,B线程打印5B,要求线程同时运行,交替打印5AB

这个问题用Objectwait()notify()就可以很方便的解决。代码如下:

 
  1. public class MyThread implements Runnable {
  2. private String name;
  3. private Object prev;
  4. private Object self;
  5. private MyThread(String name, Object prev, Object self) {
  6. this.name = name;
  7. this.prev = prev;
  8. this.self = self;
  9. }
  10. public void run() {
  11. int count = 5;
  12. while (count > 0) {
  13. synchronized (prev) {
  14. synchronized (self) {
  15. System.out.print(name);
  16. count--;
  17. self.notify();
  18. }
  19. try {
  20. prev.wait();
  21. } catch (InterruptedException e) {
  22. e.printStackT\frace();
  23. }
  24. }
  25. }
  26. System.exit(0);//退出jvm
  27. }
  28. public static void main(String[] args) throws Exception {
  29. Object a = new Object();
  30. Object b = new Object();
  31. MyThread ta = new MyThread("A", b, a);
  32. MyThread tb = new MyThread("B", a, b);
  33. new Thread(ta).start();
  34. Thread.sleep(100); //确保按顺序A、B执行
  35. new Thread(tb).start();
  36. Thread.sleep(100);
  37. }
  38. }

运行程序,结果为:

ABABABABAB

线程常用函数总结

下表列出的是线程类常用的函数:

函数描述
public void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
public void run()如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
public final void setName(String name)改变线程名称,使之与参数 name 相同。
public final void setPriority(int priority)更改线程的优先级。
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。
public final void join(long millisec)等待该线程终止的时间最长为 millis 毫秒
public void interrupt()中断线程。
public final boolean isAlive()测试线程是否处于活动状态。
public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
public static void sleep(long millisec)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
public static boolean holdsLock(Object x)当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
public static Thread currentThread()返回对当前正在执行的线程对象的引用。
public static void dumpStack()将当前线程的堆栈跟踪打印至标准错误流。
编程要求

请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,具体任务如下:

  • 建立三个线程,A线程打印5E,B线程打印5DC线程打印5U,要求线程同时运行,交替打印5EDU
测试说明

补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。


开始你的任务吧,祝你成功!

package step3;
public class MyThread implements Runnable {   
//请在此添加实现代码
/********** Begin **********/
private String name;
	private Object prev;
	private Object self;
	private MyThread(String name,Object prev,Object self){
		this.name = name;
		this.prev = prev;
		this.self = self;
	}
	public void run(){
		int count = 5;
		while(count>0){
			synchronized(prev){
				synchronized(self){
					System.out.print(name);
					count--;
					self.notify();
				}
				try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
				}
			}
		}
		System.exit(0);
	}
 	public static void main(String[] args) throws Exception {   
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
		MyThread ta = new MyThread("E",c,a);
		MyThread tb = new MyThread("D",a,b);
		MyThread tc = new MyThread("U",b,c);
		new Thread(ta).start();
		Thread.sleep(100);
		new Thread(tb).start();
		Thread.sleep(100);
		new Thread(tc).start();
		Thread.sleep(100);
    }   
/********** End **********/  
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值