分类: Java
2006-05-11 15:48:09
线程
Java语言中一个十分重要的特点就是支持多线程编程。多线程程序包含两条或两条以上并发运行的部分。程序中每个这样的部分都叫做一个线程thread,每个线程都有独立的执行路径。因此,多线程是多任务处理的一个特殊形式。
9.1 创建进程
通常,通过实例化一个Thread对象来创建一个线程,Java对此定义了两种方式。下面我们分别介绍一下。
☆ 实现Runnable接口
创建线程的最简单的方法是创建一个实现Runnable接口的类。创建过程大致分以下的几步。
首先,你通过实现Runnable接口的run()方法来创建每一个对象的线程。run()方法能够像主线程那样调用其他方法,引用其他类,声明变量。唯一不同的是,run()在程序中确定另一个并发的线程执行入口。当run()方法返回时,该线程即结束。
其次,你要在你创建的实现Runnable接口的类中,实例化一个Thread类的对象。
最后,实例化了Thread类创建了一个线程后,线程并没有运行。你要运行你创建的这个线程还要调用它的start()方法。实际上,start()执行的就是一个对run()的调用。
☆ 扩展Thread类
创建线程的另一个方法是创建一个新类来扩展继承Thread类,然后再创建这个类的实例。当这个子类继承Thread类时,它必须重载run()方法,这个run()方法就是新线程的入口。另外它同样也必须调用start()方法去启动新线程的执行。
9.2 线程的同步
当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一时刻仅被一个线程占用。这个过程就成为同步(synchronization)。
与其他语言不同的是,Java提供了一种语言上对同步的支持,极大的简化了管理线程同步的复杂性。有两种方法来同步化代码,两者都包括了synchronized关键字的运用,下面分别说明。
☆ 使用同步方法
在多线程的情况下,你如果有一个或多个方法操纵对象的内部状态,都必须使用synchronized关键字来声明方法为同步方法,防止状态出现竞争。一旦线程进入实例的同步方法,就没有其他线程可以进入相同实例的同步方法。
☆ 使用同步语句
当我们在类的定义中没有用到同步方法的时候,你可能也需要对一些操纵对象内部状态的代码同步化。这时你可以使用synchronized来声明一个同步语句块。
下面给出了synchronized语句的普通格式:
synchronized(object) {
// statement to be synchronized
}
9.3 线程间通信
对于多线程的管理,一般采用一种轮询的方式。轮询通常由重复监测条件的循环实现。一旦条件成立,就采用适当的动作。这种方式相对简单,但却很大程度上浪费了CPU的时间。Java语言则采用了另外一种机制,避免了轮询。它通过wait()、notify()和notifyAll()方法实现一个进程间通信机制。这些方法是在Object类中用final声明定义了的,所以所有的类都包含它们。它们的意义分别如下:
☆ wait():通知被调用的线程放弃管程进入睡眠直到其他线程进入相同管程并调用notify()把它唤醒。
☆ notify():恢复相同对象中第一个调用wait()的线程。
☆ notifyAll():恢复相同对象中所有调用wait()的线程。
这里需要提醒的是,这三个方法仅在synchronized方法中才能被调用。
9.4 死锁
这里,我们介绍一个死锁的概念。这是我们需要避免在多任务处理中出现的情况。
死锁发生在当两个线程对一对有依赖循环时。例如,假设一个线程进入了对象A的管程而另一个线程则进入了对象B的管程。如果A的线程试图调用Y的同步方法,它将被锁定。这时,如果B的线程希望调用A的一些同步方法,线程就会永远等待。系统陷入死锁的情况。因为为到达A,B必须释放自己的锁定以使第一个线程可以完成。
9.5 优先级问题
线程优先级被线程调度用来判断何时每个线程允许运行。理论上,优先级高的线程比优先级低的线程获得更多的CPU时间。但实际上,线程获得CPU时间通常由包括优先级在内的多个因素决定的。
我们可以使用setPriority()方法来设置线程的优先级,该方法是Thread类的成员。它的通常格式如下:
final void setPriority(int level)
level指定了对调用线程的新的优先级的设置。Level的值必须在MIN_PRIORITY 到MAX_PRIORITY之间。通常它们的值是1和10。如果要指定默认的优先级使用NORM_PRIORITY,通常值为5。
另外你还可以使用getPriority()方法来获得当前的优先级设置。