实战Java多线程编程精要之实现线程
副标题#e#
利用 Java 编程语言实现线程
Java编程语言使多线程如此简朴有效,乃至于某些措施员说它实际上是自然的。尽量在 Java 中利用线程比在其他语言中要容易得多,仍然有一些观念需要把握。要记着的一件重要的工作是 main() 函数也是一个线程,并可用来做有用的事情。措施员只有在需要多个线程时才需要建设新的线程。
Thread 类
Thread 类是一个详细的类,即不是抽象类,该类封装了线程的行为。要建设一个线程,措施员必需建设一个从 Thread 类导出的新类。措施员必需包围 Thread 的 run() 函数来完成有用的事情。用户并不直接挪用此函数;而是必需挪用 Thread 的 start() 函数,该函数再挪用 run()。下面的代码说明白它的用法:
建设两个新线程
import java.util.*;
class TimePrinter extends Thread {
int pauseTime;
String name;
public TimePrinter(int x, String n) {
pauseTime = x;
name = n;
}
public void run() {
while(true) {
try {
System.out.println(name + ":" + new Date(System.currentTimeMillis()));
Thread.sleep(pauseTime);
} catch(Exception e) {
System.out.println(e);
}
}
}
static public void main(String args[]) {
TimePrinter tp1 = new TimePrinter(1000, "Fast Guy");
tp1.start();
TimePrinter tp2 = new TimePrinter(3000, "Slow Guy");
tp2.start();
}
}
在本例中,我们可以看到一个简朴的措施,它按两个差异的时距离断(1 秒和 3 秒)在屏幕上显示当前时间。这是通过建设两个新线程来完成的,包罗 main() 共三个线程。可是,因为有时要作为线程运行的类大概已经是某个类条理的一部门,所以就不能再按这种机制建设线程。固然在同一个类中可以实现任意数量的接口,但 Java 编程语言只答允一个类有一个父类。同时,某些措施员制止从 Thread 类导出,因为它强加了类条理。对付这种环境,就要 runnable 接口。
Runnable 接口
此接口只有一个函数,run(),此函数必需由实现了此接口的类实现。可是,就运行这个类而论,其语义与前一个示例稍有差异。我们可以用 runnable 接口改写前一个示例。(差异的部门用黑体暗示。)
建设两个新线程而不强加类条理
import java.util.*;
class TimePrinter implements Runnable {
int pauseTime;
String name;
public TimePrinter(int x, String n) {
pauseTime = x;
name = n;
}
public void run() {
while(true) {
try {
System.out.println(name + ":" + new Date(System.currentTimeMillis()));
Thread.sleep(pauseTime);
} catch(Exception e) {
System.out.println(e);
}
}
}
static public void main(String args[]) {
Thread t1 = new Thread(new TimePrinter(1000, "Fast Guy"));
t1.start();
Thread t2 = new Thread(new TimePrinter(3000, "Slow Guy"));
t2.start();
}
}
#p#副标题#e#
请留意,当利用 runnable 接口时,您不能直接建设所需类的工具并运行它;必需从 Thread 类的一个实例内部运行它。很多措施员更喜欢 runnable 接口,因为从 Thread 类担任会强加类条理。
synchronized 要害字
到今朝为止,我们看到的示例都只是以很是简朴的方法来操作线程。只有最小的数据流,并且不会呈现两个线程会见同一个工具的环境。可是,在大大都有用的措施中,线程之间凡是有信息流。试思量一个金融应用措施,它有一个 Account 工具,如下例中所示:
一个银行中的多项勾当
public class Account {
String holderName;
float amount;
public Account(String name, float amt) {
holderName = name;
amount = amt;
}
public void deposit(float amt) {
amount += amt;
}
public void withdraw(float amt) {
amount -= amt;
}
public float checkBalance() {
return amount;
}
}
在此代码样例中暗藏着一个错误。假如此类用于单线程应用措施,不会有任何问题。可是,在多线程应用措施的环境中,差异的线程就有大概同时会见同一个 Account 工具,好比说一个连系帐户的所有者在差异的 ATM 上同时举办会见。在这种环境下,存入和支出就大概以这样的方法产生:一个事务被另一个事务包围。这种环境将是劫难性的。可是,Java 编程语言提供了一种简朴的机制来防备产生这种包围。每个工具在运行时都有一个关联的锁。这个锁可通过为要领添加要害字 synchronized 来得到。这样,修订过的 Account 工具(如下所示)将不会蒙受像数据损坏这样的错误:
对一个银行中的多项勾当举办同步处理惩罚
#p#分页标题#e#
public class Account {
String holderName;
float amount;
public Account(String name, float amt) {
holderName = name;
amount = amt;
}
public synchronized void deposit(float amt) {
amount += amt;
}
public synchronized void withdraw(float amt) {
amount -= amt;
}
public float checkBalance() {
return amount;
}
}
deposit() 和 withdraw() 函数都需要这个锁来举办操纵,所以当一个函数运行时,另一个函数就被阻塞。请留意, checkBalance() 未作变动,它严格是一个读函数。因为 checkBalance() 未作同步处理惩罚,所以任何其他要领都不会阻塞它,它也不会阻塞任何其他要领,不管那些要领是否举办了同步处理惩罚。