-
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
任务描述
本关任务:获取子线程执行的结果并输出。
相关知识
本关你需要掌握sleep
与join
函数的用法。
sleep()函数
sleep(long millis)
: 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。
使用方式很简单在线程的内部使用Thread.sleep(millis)
即可。
sleep()
使当前线程进入停滞状态(阻塞当前线程),让出CPU的使用,目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
在sleep()
休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
join()函数
join
函数的定义是指:等待线程终止。
我们在运行线程的时候可能会遇到,在主线程中运行子线程,主线程需要获取子线程最终执行结果的情况。
但是有很多时候子线程进行了很多耗时的操作,主线程往往先于子线程结束,这个时候主线程就获取不到子线程的最终执行结果了。
使用join
函数,可以解决这一问题。
我们通过两个示例来理解:
不使用join
函数的版本:
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
System.out.println("子线程开始运行");
for (int i = 0; i < 5; i++) {
System.out.println("子线程" + name + "运行" + i);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackT\frace();
}
}
System.out.println("子线程结束");
}
}
public class Test {
public static void main(String[] args) {
Thread t = new MyThread("子线程A");
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行" + i);
}
System.out.println("主线程结束");
}
}
输出结果(每次都不一样):
主线程执行0
主线程执行1
主线程执行2
主线程执行3
主线程执行4
主线程结束
子线程开始运行
子线程子线程A运行0
子线程子线程A运行1
子线程子线程A运行2
子线程子线程A运行3
子线程子线程A运行4
子线程结束
可以发现每次运行,主线程都是先于子线程结束的。
使用join
函数:
package step1;
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
System.out.println("子线程开始运行");
for (int i = 0; i < 5; i++) {
System.out.println("子线程" + name + "运行" + i);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackT\frace();
}
}
System.out.println("子线程结束");
}
}
public class Test {
public static void main(String[] args) {
Thread t = new MyThread("子线程A");
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行" + i);
}
try {
t.join();
} catch (Exception e) {
e.printStackT\frace();
}
System.out.println("主线程结束");
}
}
输出结果:
主线程执行0
主线程执行1
主线程执行2
主线程执行3
子线程开始运行
主线程执行4
子线程子线程A运行0
子线程子线程A运行1
子线程子线程A运行2
子线程子线程A运行3
子线程子线程A运行4
子线程结束
主线程结束
可以发现无论运行多少次,主线程都会等待子线程结束之后在结束。
编程要求
请仔细阅读右侧代码,根据方法内的提示,在Begin - End
区域内进行代码补充,具体任务如下:
- 创建自定义线程,实现求第
num
项斐波那契数列的值num
从0
开始,并且在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
任务描述
本关任务:使用三个线程交替打印5
次EDU
。
相关知识
为了完成本关任务你需要掌握:
1.yield
函数的用法;
2.wait
函数的用法;
3.其他常用函数的使用。
yield 函数
yield
函数可以理解为“让步”,它的作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()
应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的
目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()
达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
示例:
public class Task {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
MyThread mTh1=new MyThread("A");
MyThread mTh2=new MyThread("B");
mTh1.start();
mTh2.start();
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(name + "线程" + "执行" + i);
if(i == 10){
this.yield();
}
}
}
}
运行结果有两种情况:
第一种情况: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线程打印5
次A
,B线程打印5
次B
,要求线程同时运行,交替打印5
次AB
。
这个问题用Object
的wait()
,notify()
就可以很方便的解决。代码如下:
public class MyThread implements Runnable {
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.printStackT\frace();
}
}
}
System.exit(0);//退出jvm
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
MyThread ta = new MyThread("A", b, a);
MyThread tb = new MyThread("B", a, b);
new Thread(ta).start();
Thread.sleep(100); //确保按顺序A、B执行
new Thread(tb).start();
Thread.sleep(100);
}
}
运行程序,结果为:
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
线程打印5
次E
,B线程打印5
次D
,C
线程打印5
次U
,要求线程同时运行,交替打印5
次EDU
。
测试说明
补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。
开始你的任务吧,祝你成功!
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 **********/
}