程,须完成下列步骤:创建一个类扩展 Thread 类(extends Thread)。
用要在这个线程中执行的代码编写 run方法,覆盖 Thread 类的 run方法。
用关键字 new 创建所定义的线程类的一个对象。
调用该对象的 start方法启动线程。
由于 Java 只支持单继承,所定义的类不能再继承其他类。
【例 8.1】 创建一个 Thread 类的子类,显示 18 这 8 个数字,然后在另一个类中建立这个 Thread 类的 3 个对象来测试它,看执行时会发生什么现象。
终止线程执行的技巧如下步骤实现一种简单的机制,可在任何时候终止线程: 编写一个类扩展 Thread 类。
增加一个布尔变量 running 到这个类中,并初始化为 false。
覆盖 start方法:置 running 为 true,然后调用 super.start。
提供一个 public 方法 halt,它将 running 变量置为 false。
在 run方法中使用类似于下例的 while 循环:public void run while running / 线程要执行的代码 / 如果 halt方法被调用,就会引起 run方法终止执行,结束该线程。
【例 8.2】 模拟一个笼子内有二十个鸟在里面移动,每个“鸟”是一个 Thread 对象。
“笼子”是一个Frame。
它包含有 3 个按钮,用于启动、终止“鸟”和退出程序。
“鸟”类可按如下步骤去实现: 扩展 Thread,这样就是独立执行的线程,即鸟可独立行走。
按上述终止线程的技巧,使得鸟类可在任何时刻停止执行。
设置两个域 x,y,作为鸟的当前坐标。
编写 move方法,重新移动鸟到一个新的 x,y 坐标,在方法中重新计算它的坐标,使它产生移动效果。
由于有多个移动的鸟,所以在每个线程的 run方法中应该调用 sleep方法,让出时间使操作系统去移动其他的鸟。
8.2.2 通过实现 Runnable 接口来构造线程Runnable 接口Runnable 接口只有一个方法 run所有实现 Runnabel 接口的用户类都必须实现这个 run方法,即为它编写具体的方法体代码。
在许多情况下,一个类已经扩展了 Frame 或 Applet,由于单继承性,这样的类就不能再继承 Thread。
Runnable 接口为一个类提供了一种手段,无须扩展 Thread 类就可以创建一个新的线程。
从而克服了单一继承方式所造成的限制。
所有实现了 Runnable 接口的类的对象都能以线程方式执行。
使用 Runnable 接口构造线程要在一个类中实现 public void run方法,并建立一个 Thread 对象作为该类的成员变量。
实现 Runnable 接口的类的一般框架为:modifier class ClassName extends SuperClassName implements Runnable OtherInterfaceList // 域,构造方法和其他方法 Thread threadobj //建立一个 Thread 对象作为该类的成员变量 SomeMethod threadobj new Threadthis //调用 Thread 的构造方法实例化一个对象 public void run / run 方法的代码 / 【例 8.3】 创建一个程序,用实现 Runnable 接口的方法来完成每秒显示当前时间。
在 Java 的 java.util 包中有一个 Date 类,实例化一个 Date 对象可得到当前时间,再用 toString方法可将其变成字符串。
下面要确定是否需要一个线程来完成这个任务。
要求每秒显示一次时间,线程是完成这个任务最好的角色。
可以每秒执行线程一次,且在这一瞬间显示出时间,其他时间可让线程休眠。
然后要考虑是需要扩展 Thread 类还是使用 Runnable 接口。
假如这个时钟要以窗口的形式出现,则这个线程必须选择使用 Runnable 接口来实现。
ShowSeconds.java两种创建线程方式的比较使用 Runnable 接口 可以将虚拟 CPU,代码和数据分开,形成清晰的模型 还可以类继承其他类 保持程序风格的一致性。
继承 Thread 类 不能再从其他类继承 编写简单,可以直接操纵线程。
无论采取上述的哪种方式,程序员可控制的关键操作有两个: 定义用户线程的操作,即定义用户线程的 run方法; 在适当的时候建立用户线程实例。
8.3 线程的同步线程间的资源互斥共享 通常,一些同时运行的线程需要共享数据。
在这种时候,每个线程就必须考虑其它一起共享数据的线 程的状态与行为,否则的话就不能保证共享数据的一致性以及程序的正确性。
在 Java 语言中,引入了“对象互斥锁”的概念又称为管程来实现不同线程对共享数据操作的同步。
“对象互斥锁”阻止多个线程同时访问同一个方法。
在 Java 语言中,实现“对象互斥锁”的方式一般为:用关键字 synchronized 来声明一个操作共享数据的方法或一段代码。
线程间的同步 除了要处理多线程间共享数据操作的同步问题之外,在进行多线程程序设计时,还会遇到另一类问题, 这就是如何控制相互交互的线程之间的运行进度,即多线程的同步。
典型的模型:生产者——消费者问题若共享对象中只能存放一个数据,可能出现以下问题: 生产者比消费者快时,消费者会漏掉一些数据没有取到; 消费者比生产者快时,消费者取相同的数据。
8.3.1 使用多线程不当造成的数据不一致多线程使用不当,则有可能造成数据不一致。
例如,两个线程都要访问同一个整数域,一个线程读取这个域的值并在这个值的基础上完成某些操作,而另一个线程改变了这个整数值,但第一个线程并不知道,这就可能造成数据不一致。
【例 8.4】 创建一个类,它包含一个具有随机值的域 number 和一个 performWork方法,该方法完成两个任务:一个“慢”任务和一个“快”任务。
完成这些任务并不是直接调用这个方法,而是建立两个线程对象:一个快线程,一个慢线程。
“慢”线程调用 performwork 并显示一个随机数,然后让这个线程休眠 2 秒钟,以模拟一个需要较长时间才能完成的任务,然后再次显示这个 number。
“快”线程在 performwork 中将 number 修改为-1,该线程没用延时,以模拟一个能够很快完成的任务。
8.3.1 使用多线程不当造成的数据崩溃【例 8.5】建立一个银行(Bank)类,它包含 8 个储蓄账号。
每个账号最初有 10 万元余额。
Bank 类包含一个转账的 transfer 方法,可以将钱从一个储蓄账号转到其他账号。
储户线程使用 transfer方法不断从他们的账号中转出随机数目(但不超过 1000 元)的钱到其他随机选定的账号。
transfer方法需要 3 个输入参数:从中取钱的账号,存钱的账号,转账的金额。
Bank 类还有一个 showAccounts方法显示所有的账户的总金额。
Bank 类创建 8 个线程对象模拟八个客户。
这个程序在一个 Frame 中执行,并设置按钮来重新进行计算和显示各账号的储蓄总额。
8.3.2 同步线程为了解决所出现的问题,Java 提供了线程的同步机制用于保护线程共享数据,控制和切换线程的执行,保证内存的一致性,声明 synchronized方法的一般格式为:synchronized returnType methodNameparameterList / 方法体 / synchronized 关键字除了可以放在方法声明中表示整个方法为同步方法外,还可以放在一段代码的前面限制它的执行,如:modifier returnType methodNameparameterList synchronized(this) / some codes / 另外,如果 synchronized 用在类声明中,则表明该类中的所有方法都是 synchronized 的。
例8.6 例 8.7在 Java 语言中还可以用 wait 和 notify /notifyAll 方法用来协调线程间的读取关系。
wait 方法的作用是让当前线程释放其所持有的“对象互斥锁”,进入 wait 队列等待队列; notify /notifyAll 方法的作用是唤醒一个或所有正在等待队列中等待的线程,并将它们移入等待 同一个“对象互斥锁”的队列。
需要指出的是: notify/notifyAll方法和 wait 方法都只能在被声明为 synchronized 的方法或代码段中调用。
异常的概念在进行程序设计时,错误的产生
上一篇:
【精品】在VC中调用 WebService
下一篇:
心情不好的时候看看这篇日志然后擦干眼泪……