友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第118部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
try {
sleep(500);
} catch (InterruptedException e){}
}
}
public void synchTest() {
Sharing1。incrementAccess();
if(count1 != count2)
l。setText(〃Unsynched〃);
}
}
class Watcher extends Thread {
private Sharing1 p;
public Watcher(Sharing1 p) {
this。p = p;
start();
}
public void run() {
while(true) {
for(int i = 0; i 《 p。s。length; i++)
p。s'i'。synchTest();
try {
sleep(500);
} catch (InterruptedException e){}
}
}
}
public class Sharing1 extends Applet {
TwoCounter'' s;
private static int accessCount = 0;
private static TextField aCount =
new TextField(〃0〃; 10);
500
…………………………………………………………Page 502……………………………………………………………
public static void incrementAccess() {
accessCount++;
aCount。setText(Integer。toString(accessCount));
}
private Button
start = new Button(〃Start〃);
observer = new Button(〃Observe〃);
private boolean isApplet = true;
private int numCounters = 0;
private int numObservers = 0;
public void init() {
if(isApplet) {
numCounters =
Integer。parseInt(getParameter(〃size〃));
numObservers =
Integer。parseInt(
getParameter(〃observers〃));
}
s = new TwoCounter'numCounters';
for (int i = 0; i 《 s。length; i++)
s'i' = new TwoCounter(this);
Panel p = new Panel();
start。addActionListener(new StartL());
p。add(start);
observer。addActionListener(new ObserverL());
p。add(observer);
p。add(new Label(〃Access Count〃));
p。add(aCount);
add(p);
}
class StartL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i 《 s。length; i++)
s'i'。start();
}
}
class ObserverL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i 《 numObservers; i++)
new Watcher(Sharing1。this);
}
}
public static void main(String'' args) {
Sharing1 applet = new Sharing1();
// This isn't an applet; so set the flag and
// produce the parameter values from args:
applet。isApplet = false;
applet。numCounters =
(args。length == 0 ? 5 :
Integer。parseInt(args'0'));
applet。numObservers =
(args。length 《 2 ? 5 :
501
…………………………………………………………Page 503……………………………………………………………
Integer。parseInt(args'1'));
Frame aFrame = new Frame(〃Sharing1〃);
aFrame。addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e){
System。exit(0);
}
});
aFrame。add(applet; BorderLayout。CENTER);
aFrame。setSize(350; applet。numCounters *100);
applet。init();
applet。start();
aFrame。setVisible(true);
}
} ///:~
和往常一样,每个计数器都包含了自己的显示组件:两个文本字段以及一个标签。根据它们的初始值,可知
道计数是相同的。这些组件在 TwoCounter 构建器加入 Container。由于这个线程是通过用户的一个“按下按
钮”操作启动的,所以 start()可能被多次调用。但对一个线程来说,对 Thread。start()的多次调用是非法
的(会产生违例)。在 started标记和过载的 start()方法中,大家可看到针对这一情况采取的防范措施。
在run()中,count1 和 count2 的增值与显示方式表面上似乎能保持它们完全一致。随后会调用sleep();若
没有这个调用,程序便会出错,因为那会造成 CPU 难于交换任务。
synchTest()方法采取的似乎是没有意义的行动,它检查 count1 是否等于count2;如果不等,就把标签设为
“Unsynched”(不同步)。但是首先,它调用的是类Sharing1 的一个静态成员,以便增值和显示一个访问
计数器,指出这种检查已成功进行了多少次(这样做的理由会在本例的其他版本中变得非常明显)。
Watcher 类是一个线程,它的作用是为处于活动状态的所有 TwoCounter 对象都调用 synchTest()。其间,它
会对Sharing1 对象中容纳的数组进行遍历。可将 Watcher 想象成它掠过 TwoCounter 对象的肩膀不断地“偷
看”。
Sharing1 包含了 TwoCounter 对象的一个数组,它通过 init()进行初始化,并在我们按下“start”按钮后作
为线程启动。以后若按下“Observe”(观察)按钮,就会创建一个或者多个观察器,并对毫不设防的
TwoCounter 进行调查。
注意为了让它作为一个程序片在浏览器中运行,Web 页需要包含下面这几行:
可自行改变宽度、高度以及参数,根据自己的意愿进行试验。若改变了size 和 observers,程序的行为也会
发生变化。我们也注意到,通过从命令行接受参数(或者使用默认值),它被设计成作为一个独立的应用程
序运行。
下面才是最让人“不可思议”的。在TwoCounter。run()中,无限循环只是不断地重复相邻的行:
t1。setText(Integer。toString(count1++));
t2。setText(Integer。toString(count2++));
(和“睡眠”一样,不过在这里并不重要)。但在程序运行的时候,你会发现count1和 count2 被“观察”
(用Watcher 观察)的次数是不相等的!这是由线程的本质造成的——它们可在任何时候挂起(暂停)。所
以在上述两行的执行时刻之间,有时会出现执行暂停现象。同时,Watcher 线程也正好跟随着进来,并正好
在这个时候进行比较,造成计数器出现不相等的情况。
本例揭示了使用线程时一个非常基本的问题。我们跟无从知道一个线程什么时候运行。想象自己坐在一张桌
子前面,桌上放有一把叉子,准备叉起自己的最后一块食物。当叉子要碰到食物时,食物却突然消失了(因
为这个线程已被挂起,同时另一个线程进来“偷”走了食物)。这便是我们要解决的问题。
有的时候,我们并不介意一个资源在尝试使用它的时候是否正被访问(食物在另一些盘子里)。但为了让多
502
…………………………………………………………Page 504……………………………………………………………
线程机制能够正常运转,需要采取一些措施来防止两个线程访问相同的资源——至少在关键的时期。
为防止出现这样的冲突,只需在线程使用一个资源时为其加锁即可。访问资源的第一个线程会其加上锁以
后,其他线程便不能再使用那个资源,除非被解锁。如果车子的前座是有限的资源,高喊“这是我的!”的
孩子会主张把它锁起来。
14。2。2 Java 如何共享资源
对一种特殊的资源——对象中的内存——Java 提供了内建的机制来防止它们的冲突。由于我们通常将数据元
素设为从属于private (私有)类,然后只通过方法访问那些内存,所以只需将一个特定的方法设为
synchronized (同步的),便可有效地防止冲突。在任何时刻,只可有一个线程调用特定对象的一个
synchronized方法(尽管那个线程可以调用多个对象的同步方法)。下面列出简单的synchronized 方法:
synchronized void f() { /* 。。。 */ }
synchronized void g() { /* 。。。 */ }
每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代
码)。调用任何 synchronized方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized 方
法,除非第一个方法完成了自己的工作,并解除锁定。在上面的例子中,如果为一个对象调用f(),便不能
再为同样的对象调用g(),除非 f()完成并解除锁定。因此,一个特定对象的所有 synchronized方法都共享
着一把锁,而且这把锁能防止多个方法对通用内存同时进行写操作(比如同时有多个线程)。
每个类也有自己的一把锁(作为类的Class 对象的一部分),所以synchronized static 方法可在一个类的
范围内被相互间锁定起来,防止与 static数据的接触。
注意如果想保护其他某些资源不被多个线程同时访问,可以强制通过 synchronized 方访问那些资源。
1。 计数器的同步
装备了这个新关键字后,我们能够采取的方案就更灵活了:可以只为 TwoCounter 中的方法简单地使用
synchronized关键字。下面这个例子是对前例的改版,其中加入了新的关键字:
//: Sharing2。java
// Using the synchronized keyword to prevent
// multiple access to a particular resource。
import java。awt。*;
import java。awt。event。*;
import java。applet。*;
class TwoCounter2 extends Thread {
private boolean started = false;
private TextField
t1 = new TextField(5);
t2 = new TextField(5);
private Label l =
new Label(〃count1 == count2〃);
private int count1 = 0; count2 = 0;
public TwoCounter2(Container c) {
Panel p = new Panel();
p。add(t1);
p。add(t2);
p。add(l);
c。add(p);
}
public void start() {
if(!started) {
started = true;
super。start();
}
503
…………………………………………………………Page 505……………………………………………………………
}
public synchronized void run() {
while (true) {
t1。setText(Integer。toString(count1++));
t2。setText(Integer。toString(count2++));
try {
sleep(500);
} catch (InterruptedException e){}
}
}
public synchronized void synchTest() {
Sharing2。incrementAccess();
if(count1 != count2)
l。setText(〃Unsynched〃);
}
}
class Watcher2 extends Thread {
private Sharing2 p;
public Watcher2(Sharing2 p) {
this。p = p;
start();
}
public void run() {
while(true) {
for(int i = 0; i 《 p。s。length; i++)
p。s'i'。synchTest();
try {
sleep(500);
} catch (InterruptedException e){}
}
}
}
public class Sharing2 extends Applet {
TwoCounter2'' s;
private static int accessCount = 0;
private static TextField aCount =
new TextField(〃0〃; 10);
public static void incrementAccess() {
accessCount++;
aCount。setText(Integer。toString(accessCount));
}
private Button
start = new Button(〃Start〃);
observer = new Button(〃Observe〃);
private boolean isApplet = true;
private int numCounters = 0;
private int numObservers = 0;
public void init() {
if(isApplet) {
numCounters =
504
…………………………………………………………Page 506……………………………………………………………
Integer。parseInt(getParameter(〃size〃));
numObservers =
Integer。parseInt(
getParameter(〃observers〃));
}
s = new TwoCounter2'numCounters';
for(int i = 0; i 《 s。length; i++)
s'i' = new TwoCounter2(this);
Panel p = new Panel();
start。addActionListener(new StartL());
p。add(start);
observer。addActionListener(new ObserverL());
p。add(observer);
p。add(new Label(〃Access Count〃));
p。add(aCount);
add(p);
}
class StartL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i 《 s。length; i++)
s'i'。start();
}
}
class Ob
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!