📄 chapter14.html
字号:
adjust the same valve,
etc.</FONT><A NAME="_Toc375545479"></A><A NAME="_Toc408018752"></A><BR></P></DIV>
<A NAME="Heading489"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Improperly accessing resources</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Consider a variation on the
counters that have been used so far in this chapter. In the following example,
each thread contains two counters that are incremented and displayed inside
<B>run( )</B>. In addition, there’s another thread of class
<B>Watcher</B> that is watching the counters to see if they’re always
equivalent. This seems like a needless activity, since looking at the code it
appears obvious that the counters will always be the same. But that’s
where the surprise comes in. Here’s the first version of the
program:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: Sharing1.java</font>
<font color=#009900>// Problems with resource sharing while threading</font>
<font color=#0000ff>import</font> java.awt.*;
<font color=#0000ff>import</font> java.awt.event.*;
<font color=#0000ff>import</font> java.applet.*;
<font color=#0000ff>class</font> TwoCounter <font color=#0000ff>extends</font> Thread {
<font color=#0000ff>private</font> <font color=#0000ff>boolean</font> started = <font color=#0000ff>false</font>;
<font color=#0000ff>private</font> TextField
t1 = <font color=#0000ff>new</font> TextField(5),
t2 = <font color=#0000ff>new</font> TextField(5);
<font color=#0000ff>private</font> Label l =
<font color=#0000ff>new</font> Label(<font color=#004488>"count1 == count2"</font>);
<font color=#0000ff>private</font> <font color=#0000ff>int</font> count1 = 0, count2 = 0;
<font color=#009900>// Add the display components as a panel</font>
<font color=#009900>// to the given container:</font>
<font color=#0000ff>public</font> TwoCounter(Container c) {
Panel p = <font color=#0000ff>new</font> Panel();
p.add(t1);
p.add(t2);
p.add(l);
c.add(p);
}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> start() {
<font color=#0000ff>if</font>(!started) {
started = <font color=#0000ff>true</font>;
<font color=#0000ff>super</font>.start();
}
}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> run() {
<font color=#0000ff>while</font> (<font color=#0000ff>true</font>) {
t1.setText(Integer.toString(count1++));
t2.setText(Integer.toString(count2++));
<font color=#0000ff>try</font> {
sleep(500);
} <font color=#0000ff>catch</font> (InterruptedException e){}
}
}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> synchTest() {
Sharing1.incrementAccess();
<font color=#0000ff>if</font>(count1 != count2)
l.setText(<font color=#004488>"Unsynched"</font>);
}
}
<font color=#0000ff>class</font> Watcher <font color=#0000ff>extends</font> Thread {
<font color=#0000ff>private</font> Sharing1 p;
<font color=#0000ff>public</font> Watcher(Sharing1 p) {
<font color=#0000ff>this</font>.p = p;
start();
}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> run() {
<font color=#0000ff>while</font>(<font color=#0000ff>true</font>) {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < p.s.length; i++)
p.s[i].synchTest();
<font color=#0000ff>try</font> {
sleep(500);
} <font color=#0000ff>catch</font> (InterruptedException e){}
}
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Sharing1 <font color=#0000ff>extends</font> Applet {
TwoCounter[] s;
<font color=#0000ff>private</font> <font color=#0000ff>static</font> <font color=#0000ff>int</font> accessCount = 0;
<font color=#0000ff>private</font> <font color=#0000ff>static</font> TextField aCount =
<font color=#0000ff>new</font> TextField(<font color=#004488>"0"</font>, 10);
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> incrementAccess() {
accessCount++;
aCount.setText(Integer.toString(accessCount));
}
<font color=#0000ff>private</font> Button
start = <font color=#0000ff>new</font> Button(<font color=#004488>"Start"</font>),
observer = <font color=#0000ff>new</font> Button(<font color=#004488>"Observe"</font>);
<font color=#0000ff>private</font> <font color=#0000ff>boolean</font> isApplet = <font color=#0000ff>true</font>;
<font color=#0000ff>private</font> <font color=#0000ff>int</font> numCounters = 0;
<font color=#0000ff>private</font> <font color=#0000ff>int</font> numObservers = 0;
<font color=#0000ff>public</font> <font color=#0000ff>void</font> init() {
<font color=#0000ff>if</font>(isApplet) {
numCounters =
Integer.parseInt(getParameter(<font color=#004488>"size"</font>));
numObservers =
Integer.parseInt(
getParameter(<font color=#004488>"observers"</font>));
}
s = <font color=#0000ff>new</font> TwoCounter[numCounters];
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < s.length; i++)
s[i] = <font color=#0000ff>new</font> TwoCounter(<font color=#0000ff>this</font>);
Panel p = <font color=#0000ff>new</font> Panel();
start.addActionListener(<font color=#0000ff>new</font> StartL());
p.add(start);
observer.addActionListener(<font color=#0000ff>new</font> ObserverL());
p.add(observer);
p.add(<font color=#0000ff>new</font> Label(<font color=#004488>"Access Count"</font>));
p.add(aCount);
add(p);
}
<font color=#0000ff>class</font> StartL <font color=#0000ff>implements</font> ActionListener {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> actionPerformed(ActionEvent e) {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < s.length; i++)
s[i].start();
}
}
<font color=#0000ff>class</font> ObserverL <font color=#0000ff>implements</font> ActionListener {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> actionPerformed(ActionEvent e) {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < numObservers; i++)
<font color=#0000ff>new</font> Watcher(Sharing1.<font color=#0000ff>this</font>);
}
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Sharing1 applet = <font color=#0000ff>new</font> Sharing1();
<font color=#009900>// This isn't an applet, so set the flag and</font>
<font color=#009900>// produce the parameter values from args:</font>
applet.isApplet = <font color=#0000ff>false</font>;
applet.numCounters =
(args.length == 0 ? 5 :
Integer.parseInt(args[0]));
applet.numObservers =
(args.length < 2 ? 5 :
Integer.parseInt(args[1]));
Frame aFrame = <font color=#0000ff>new</font> Frame(<font color=#004488>"Sharing1"</font>);
aFrame.addWindowListener(
<font color=#0000ff>new</font> WindowAdapter() {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> windowClosing(WindowEvent e){
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(350, applet.numCounters *100);
applet.init();
applet.start();
aFrame.setVisible(<font color=#0000ff>true</font>);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As before, each counter contains
its own display components: two text fields and a label that initially indicates
that the counts are equivalent. These components are added to the <B>Container
</B>in the <B>TwoCounter</B> constructor. Because this thread is started via a
button press by the user, it’s possible that <B>start( )</B> could be
called more than once. It’s illegal for <B>Thread.start( )</B> to be
called more than once for a thread (an exception is thrown). You can see that
the machinery to prevent this in the <B>started </B>flag and the overridden
<B>start( )</B> method.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>run( )</B>,
<B>count1</B> and <B>count2</B> are incremented and displayed in a manner that
would seem to keep them identical. Then
<A NAME="Index2478"></A><A NAME="Index2479"></A><B>sleep( )</B> is called;
without this call the program balks because it becomes hard for the CPU to swap
tasks.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>synchTest( )</B> method
performs the apparently useless activity of checking to see if <B>count1</B> is
equivalent to <B>count2</B>; if they are not equivalent it sets the label to
“Unsynched” to indicate this. But first, it calls a static member of
the class <B>Sharing1</B> that increments and displays an access counter to show
how many times this check has occurred successfully. (The reason for this will
become apparent in future variations of this example.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>Watcher</B> class is a
thread whose job is to call <B>synchTest( )</B> for all of the
<B>TwoCounter</B> objects that are active. It does this by stepping through the
array that’s kept in the <B>Sharing1</B> object. You can think of the
<B>Watcher</B> as constantly peeking over the shoulders of the <B>TwoCounter</B>
objects.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>Sharing1</B> contains an array
of <B>TwoCounter</B> objects that it initializes in <B>init( )</B> and
starts as threads when you press the “start” button. Later, when you
press the “Observe” button, one or more observers are created and
freed upon the unsuspecting <B>TwoCounter </B>threads.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Note that to run this as an applet
in a browser, your Web page will need to contain the lines:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><applet code=Sharing1 width=650 height=500>
<param name=size value=<font color=#004488>"20"</font>>
<param name=observers value=<font color=#004488>"1"</font>>
</applet></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can change the width, height,
and parameters to suit your experimental tastes. By changing the <B>size</B> and
<B>observers</B> you’ll change the behavior of the program. You can also
see that this program is set up to run as a stand-alone application by pulling
the arguments from the command line (or providing defaults).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s the surprising part.
In <B>TwoCounter.run( )</B>, the infinite loop is just repeatedly passing
over the adjacent lines:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>t1.setText(Integer.toString(count1++));
t2.setText(Integer.toString(count2++));</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">(as well as sleeping, but
that’s not important here). When you run the program, however,
you’ll discover that <B>count1</B> and <B>count2</B> will be observed (by
the <B>Watcher</B>) to be unequal at times! This is because of the nature of
threads – they can be <A NAME="Index2480"></A>suspended at any time. So at
times, the suspension occurs <I>between</I> the execution of the above two
lines, and the <B>Watcher</B> thread happens to come along and perform the
comparison at just this moment, thus finding the two counters to be
different.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This example shows a fundamental
problem with using threads. You never know when a thread might be run. Imagine
sitting at a table with a fork, about to spear the last piece of food on your
plate and as your fork reaches for it, the food suddenly vanishes (because your
thread was suspended and another thread came in and stole the food).
That’s the problem that you’re dealing with.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Sometimes you don’t care if a
resource is being accessed at the same time you’re trying to use it (the
food is on some other plate). But for multithreading to work, you need some way
to prevent two threads from accessing the same resource, at least during
critical periods.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Preventing this kind of collision
is simply a matter of putting a lock on a resou
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -