background image

法保证何时运行一个给定线程)。对于每个线程可以使一个优先级与之相关联以确保关键
线程被 JVM 处理在次要的线程之前。
  启动一个线程的第二种方法是使用一个实现 Runnable 接口的类-这个接口也定义在
java.lang 中。这个 Runnable 接口指定一个 run()方法-然后该方法成为线程的主函数,
类似于前面的代码。
  现在,Java 程序的一般风格是支持继承的接口。通过使用接口,一个类在后面仍然
能够继承(子类化)-如果必要的话(例如,如果该类要在后面作为一个 applet 使用的话,
就会发生这种情况)。
  三、线程的含义
  在采用多线程技术增强性能的同时,它也增加了程序内部运行的复杂性。这种复杂性
主要是由线程之间的交互引起的。熟悉这些问题是很重要的,因为随着越来越多的核心芯
片加入到 Intel 处理器中,要使用的线程数目也将相应地增长。如果在创建多线程程序时
不能很好地理解这些问题,那么是调试时将很难发现错误。因此,让我们先看一下这些问
题及其解决办法。
  等待另一个线程完成:假定我们有一个整型数组要进行处理。我们可以遍历这个数组,
每次一个整数并执行相应的操作。或,更高效地,我们可以建立多个线程,这样以来让每
个线程处理数组的一部分。假定我们在开始下一步之前必须等待所有的线程结束。为了暂
时同步线程之间的活动,这些线程使用了 join()方法-它使得一个线程等待另一个线程的
完成。加入的线程(线程 B)等待被加入的线程(线程 A)的完成。在 join()中的一个可选的超
时值使得线程 B 可以继续处理其它工作-如果线程 A 在给定的时间帧内还没有终止的话。
这个问题将触及到线程的核心复杂性-等待线程的问题。下面我们将讨论这个问题。
  在锁定对象上等待:假定我们编写一个航空公司座位分配系统。在开发这种大型的程
序时,为每个连接到该软件的用户分配一个线程是很经常的,如一个线程对应一个机票
销售员(在很大的系统中,情况并非总是如此)。如果有两个用户同时想分配同一个座位,
就会出现问题。除非采取特殊的措施,否则一个线程将分配该座位而另一个线程将会在做
相同的事情。两个用户都会认为他们在这趟航班上拥有一个分配的位子。
  为了避免两个线程同时修改一样的数据项,我们让一个线程在修改数据前锁定数据
项。用这种方法,当第二个线程开始作修改时,它将等待到第一个线程释放锁为止。当这
种发生时,线程将会看到座位已被分配,而对于座位分配的请求就会失败。两个线程竞争
分配座位的问题也就是著名的竞争条件问题,而当竞争发生时有可能导致系统的泄漏。为
此,最好的办法就是锁定任何代码-该代码存取一个可由多个线程共同存取的变量。
  在 Java 中存在好几种锁选择。其中最为常用的是使用同步机制。当一个方法的签名包
含同步时,在任何给定时间只有一个线程能够执行这个方法。然后,当该方法完成执行时,
对该方法的锁定即被解除。例如,
  protected synchronized int reserveSeat ( Seat seat_number ){
  if ( seat_number.getReserved() == false ){
  seat_number.setReserved();
  return ( 0 );
  }
  else return ( -1 );
  }