java线程练习题及答案 下载本文

1. 新建状态(New Thread)

当Applet启动时调用Applet的start()方法,此时小应用程序就创建一个Thread对象clockThread。

public void start() {

if (clockThread == null) {

clockThread = new Thread(cp, \ clockThread.start(); } }

当该语句执行后clockThread就处于新建状态。处于该状态的线程仅仅是空的线程对象,并没有为其分配系统资源。当线程处于该状态,你仅能启动线程,调用任何其他方法是无意义的且会引发IllegalThreadStateException异常(实际上,当调用线程的状态所不允许的任何方法时,运行时系统都会引发IllegalThreadStateException异常)。

注意cp作为线程构造方法的第一个参数,该参数必须是实现了Runnable接口的对象并提供线程运行的run()方法,第二个参数是线程名。

2. 就绪状态(Runnable)

一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,如clockThread.start(); 语句就是启动clockThread线程。start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。

3. 运行状态(Running)

当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法,这里run()方法中是一个循环,循环条件是true。 public void run() { while (true) { repaint(); try {

Thread.sleep(1000);

} catch (InterruptedException e){} }

4. 阻塞状态(Blocked)

线程运行过程中,可能由于各种原因进入阻塞状态。所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。有关阻塞状态在后面详细讨论。

5. 死亡状态(Dead)

线程的正常结束,即run()方法返回,线程运行就结束了,此时线程就处于死亡状态。本例子中,线程运行结束的条件是clockThread为null,而在小应用程序的stop()方法中,将clockThread赋值为null。即当用户离开含有该小应用程序的页面时,浏览器调用stop()方法,将clockThread赋值为null,这样在run()的while循环时条件就为false,这样线程运行就结束了。如果再重新访问该页面,小应用程序的start()方法又会重新被调用,重新创建并启动

一个新的线程。

public void stop() {

clockThread = null; }

程序不能像终止小应用程序那样通过调用一个方法来结束线程(小应用程序通过调用stop()方法结束小应用程序的运行)。线程必须通过run()方法的自然结束而结束。通常在run()方法中是一个循环,要么是循环结束,要么是循环的条件不满足,这两种情况都可以使线程正常结束,进入死亡状态。

例如,下面一段代码是一个循环: public void run(){ int i = 0;

while(i<100){ i++;

System.out.println(\} }

当该段代码循环结束后,线程就自然结束了。注意一个处于死亡状态的线程不能再调用该线程的任何方法。 2 线程的优先级和调度

Java的每个线程都有一个优先级,当有多个线程处于就绪状态时,线程调度程序根据线程的优先级调度线程运行。

可以用下面方法设置和返回线程的优先级。

? public final void setPriority(int newPriority) 设置线程的优先级。 ? public final int getPriority() 返回线程的优先级。 newPriority为线程的优先级,其取值为1到10之间的整数,也可以使用Thread类定义的常量来设置线程的优先级,这些常量分别为:Thread.MIN_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY,它们分别对应于线程优先级的1、5和10,数值越大优先级越高。当创建Java线程时,如果没有指定它的优先级,则它从创建该线程那里继承优先级。

一般来说,只有在当前线程停止或由于某种原因被阻塞,较低优先级的线程才有机会运行。

前面说过多个线程可并发运行,然而实际上并不总是这样。由于很多计算机都是单CPU的,所以一个时刻只能有一个线程运行,多个线程的并发运行只是幻觉。在单CPU机器上多个线程的执行是按照某种顺序执行的,这称为线程的调度(scheduling)。

大多数计算机仅有一个CPU,所以线程必须与其他线程共享CPU。多个线程在单个CPU是按照某种顺序执行的。实际的调度策略随系统的不同而不同,通常线程调度可以采用两种策略调度处于就绪状态的线程。

(1) 抢占式调度策略

Java运行时系统的线程调度算法是抢占式的 (preemptive)。Java运行时系统支持一种简单的固定优先级的调度算法。如果一个优先级比其他任何处于可运行状态的线程都高的线程进入就绪状态,那么运行时系统就会选择该线程运行。新的优先级较高的线程抢占(preempt)了其他线程。但是Java运行时系统并不抢占同优先级的线程。换句话说,Java运行时系统不是分时的(time-slice)。然而,基于Java Thread类的实现系统可能是支持分时的,因此编写代码时不要依赖分时。当系统中的处于就绪状态的线程都具有相同优先级时,线程调度程序采用一种简单的、非抢占式的轮转的调度顺序。

(2) 时间片轮转调度策略

有些系统的线程调度采用时间片轮转(round-robin)调度策略。这种调度策略是从所有处于就绪状态的线程中选择优先级最高的线程分配一定的CPU时间运行。该时间过后再选择其他线程运行。只有当线程运行结束、放弃(yield)CPU或由于某种原因进入阻塞状态,低优先级的线程才有机会执行。如果有两个优先级相同的线程都在等待CPU,则调度程序以轮转的方式选择运行的线程。

线程状态的改变

一个线程在其生命周期中可以从一种状态改变到另一种状态,线程状态的变迁如图5所示:

新建状态 start() scheduler 就绪状态 yield() 运行状态 sleep() I/O操作 join() 阻塞状态 图5 线程状态的改变

wait() run() 死亡状态 1 控制线程的启动和结束

当一个新建的线程调用它的start()方法后即进入就绪状态,处于就绪状态的线程被线程调度程序选中就可以获得CPU时间,进入运行状态,该线程就开始运行run()方法。

控制线程的结束稍微复杂一点。如果线程的run()方法是一个确定次数的循环,则循环结束后,线程运行就结束了,线程对象即进入死亡状态。如果run()方法是一个不确定循环,早期的方法是调用线程对象的stop()方法,然而由于该方法可能导致线程死锁,因此从1.1版开始,不推荐使用该方法结束线程。一般是通过设置一个标志变量,在程序中改变标志变量的值实现结束线程。请看下面的例子:

程序 ThreadStop.java

import java.util.*;

class Timer implements Runnable{ boolean flag=true; public void run(){ while(flag){

System.out.print(\ try{ Thread.sleep(1000);

}catch(InterruptedException e){} }

System.out.println(\ }

public void stopRun(){

flag = false; } }

public class ThreadStop{

public static void main(String args[]){ Timer timer = new Timer();

Thread thread = new Thread(timer); thread.setName(\ thread.start();

for(int i=0;i<100;i++){ System.out.print(\ try{ Thread.sleep(100);

}catch(InterruptedException e){} }

timer.stopRun(); } }

_____________________________________________________________________________▃ 该程序在Timer类中定义了一个布而变量flag,同时定义了一个stopRun()方法,在其中将该变量设置为false。在主程序中通过调用该方法,从而改变该变量的值,使得run()方法的while循环条件不满足,从而实现结束线程的运行。

说明 在Thread类中除了stop()方法被标注为不推荐(deprecated) 使用外,suspend()方

法和resume()方法也被标明不推荐使用,这两个方法原来用作线程的挂起和恢复。 2 线程阻塞条件

处于运行状态的线程除了可以进入死亡状态外,还可能进入就绪状态和阻塞状态。下面分别讨论这两种情况:

(1) 运行状态到就绪状态

处于运行状态的线程如果调用了yield()方法,那么它将放弃CPU时间,使当前正在运行的线程进入就绪状态。这时有几种可能的情况:如果没有其他的线程处于就绪状态等待运行,该线程会立即继续运行;如果有等待的线程,此时线程回到就绪状态状态与其他线程竞争CPU时间,当有比该线程优先级高的线程时,高优先级的线程进入运行状态,当没有比该线程优先级高的线程时,但有同优先级的线程,则由线程调度程序来决定哪个线程进入运行状态,因此线程调用yield()方法只能将CPU时间让给具有同优先级的或高优先级的线程而不能让给低优先级的线程。

一般来说,在调用线程的yield()方法可以使耗时的线程暂停执行一段时间,使其他线程有执行的机会。

(2) 运行状态到阻塞状态

有多种原因可使当前运行的线程进入阻塞状态,进入阻塞状态的线程当相应的事件结束或条件满足时进入就绪状态。使线程进入阻塞状态可能有多种原因:

① 线程调用了sleep()方法,线程进入睡眠状态,此时该线程停止执行一段时间。当时间到时该线程回到就绪状态,与其他线程竞争CPU时间。

Thread类中定义了一个interrupt()方法。一个处于睡眠中的线程若调用了interrupt()方法,该线程立即结束睡眠进入就绪状态。