java多线程实例浅析Word文档下载推荐.docx
- 文档编号:16790359
- 上传时间:2022-11-26
- 格式:DOCX
- 页数:23
- 大小:84.74KB
java多线程实例浅析Word文档下载推荐.docx
《java多线程实例浅析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《java多线程实例浅析Word文档下载推荐.docx(23页珍藏版)》请在冰豆网上搜索。
8
9
private
int
count
=
0;
10
Button
onOff
new
Button("
Toggle"
),
start
Start"
);
11
TextField
t
TextField(10);
12
boolean
runFlag
true;
13
14
void
init()
15
add(t);
16
start.addActionListener(new
StartL());
17
add(start);
18
onOff.addActionListener(new
OnOffL());
19
add(onOff);
20
}
21
22
go()
23
while
(true)
24
try
25
Thread.currentThread().sleep(100);
26
}
27
catch
(InterruptedException
e)
28
29
30
if
(runFlag)
31
t.setText(Integer.toString(count++));
32
33
34
35
StartL
implements
ActionListener
36
actionPerformed(ActionEvent
37
go();
38
39
40
41
OnOffL
42
43
!
runFlag;
44
45
46
47
static
main(String[]
args)
48
applet
Counter1();
49
Frame
aFrame
Frame("
Counter1"
50
aFrame.addWindowListener(new
WindowAdapter()
51
windowClosing(WindowEvent
52
System.exit(0);
53
54
});
55
aFrame.add(applet,
BorderLayout.CENTER);
56
aFrame.setSize(300,
200);
57
applet.init();
58
applet.start();
59
aFrame.setVisible(true);
60
61
按下start,运行这个程序后会发现,当按下暂停键时,程序不会做出任何的响应——尽管我们已经给它设置了让运行停止的监听器。
而事实上是,除了
最大化最小化还有点反应外,基本上其他按键都已经失去响应了。
这个是为什么呢?
让我们从代码中取证。
12行定义了一个布尔值的flag变量,这个变量用于标识是否我们还需要更新TextField中的数值,从而让我们感觉是否计时器还在计时。
当为false时停
止计时,为true时继续计时——这是程序设计者的原意(你和我很清楚的知道,这个良好的意图泡汤了)。
22-33行是实现计时器功能的核心代码——一个叫做go的方法。
在这个方法里面有一个无限的循环,在循环里面先是通过Thread类的静态方法让当前线
程sleep了100毫秒,之后马上判断我们的flag是否为true,为true则进行TextField的内容更新,为false则停止更新。
35-39为一个内部类,这个内部类其实充当了Start键的监听器的角色。
当start键被单击时,这个监听器会被使用,并回调它里面的actionPerformed
方法。
在这个监听器里面的该方法中,直接调用了之前定义的go方法。
也就是说,当我们点击start按钮时,go方法会被调用,从而代表了我们的计时器开始
运行了。
41-45为另一个内部类的定义。
它是toggle键的监听器类。
在这个监听器里面设置了flag的值——将它取反。
也就是说,当点击toggle键时就会将flag
从true设为false(或从false设为true),这样go方法中的循环便会在紧接着的一次循环中察觉到这个改变,从而做出停止更新(或开始更新)TextField
的操作,从而看起来像是停止(或开始)了计时工作。
以上的分析从某种意义上来说,是从设计者的理想上面出发的。
真正运行这个小程序时你会发现,一旦开始之后,我们的start键、toggle键以及关闭窗
口键都已经失去了响应——情况糟糕透了。
为什么会这样呢?
我无法顺利关闭它,也无法停止它——简直是糟透了
还记得刚开始说的那段话么?
这个包含简易ui的小程序的ui控制都是包含在一个线程里面的——也就是刚才我们展示的整个代码所被执行的地方。
当我们
开始了go方法时,这个线程就专注于处理这一段代码的执行去了。
而处在同一个线程中的Button的监听等操作则被“繁忙”的线程“忽略”掉了。
而且更糟糕
的是,go方法里面是一个无限循环——也就是说,一旦我们按下了start键开始了go方法的执行,这整个代码被执行的线程便会永远的专注于这个贪婪的go方
法而无暇顾及其他的代码段——无论你是按下start键还是按下toggle键,都没有响应,因为它们不在go的考虑范围之内。
你可以想象自己是一个扫描代码的
机器,并且跟着整个代码进行一次“执行”。
到后来你会发现自己在go这个地方不断地上下点头,上下点头——这时如果一个朋友告诉你还有start监听的部
分需要你照看,你会很不耐烦的说,go这个地方已经把你完全占用了,你已经无力分身去顾及了。
刚才的代码就是一个典型的线程占用过度的例子。
这在很多包含ui的程序——例如android程序——里有体现。
例如android新手往往会把一个长时间的
http请求和ui管理放在同一个线程中,这样便导致了ui无法及时响应用户操作,导致用户觉得你的应用很慢——甚至怀疑自己机器的处理能力(-_-)。
那怎么解决刚才的那个问题呢?
答案正是本文的主题——多线程机制。
紧接下来请看如下代码:
SeparateSubTask
Thread
Counter2
c2;
SeparateSubTask(Counter2
c2)
this.c2
start();
invertFlag()
run()
sleep(100);
c2.t.setText(Integer.toString(count++));
sp
null;
(sp
==
null)
SeparateSubTask(Counter2.this);
sp.invertFlag();
62
63
Counter2();
64
Counter2"
65
66
67
68
69
70
71
72
73
74
75
76
正如刚才所说,我们的解决之道是利用java语言特性里面本身就已经包含的多线程机制。
java里面负责多线程的主要有一个接口和一个类。
接口就是java.lang.Runable接口,类就是java.lang.Thread类。
其实Thread是一个实现了Runa
ble接口的实现类,它包含了Runable接口的一些默认实现以及一些其他的工具类方法(例如开始线程的方法)供开发人员使用。
Runable接口只定义一个方法:
voidrun()。
在run方法中的代码段就是我们自定义的线程所要单独执行的代码段。
既然这些都明了了,那我们就去看看刚才的代码。
7-33行是一个继承自Thread类的新的线程类。
在这个类里面我们重写了Thread的run方法,从而让我们自己的线程类拥有我们想要的线程行为。
8-10为
该线程类所包含的一些必要字段。
count就是我们将要不断自增后显示出来的时间值。
c2是一个Counter2的引用,负责将我们的线程和Counter2类的示例联
系起来,从而可以让线程类能够更新Counter2示例中的TextField的显示——这段代码见30行。
另外在12-15行定义了我们线程类的构造器,我们先将c2进
行了初始化赋值,以将我们即将构造的线程类的示例和某个Counter2示例“挂钩”起来,紧接着我调用了SeparateSubTask的父类方法start()方法,从而
启动了线程——如此一来,我们构造SeparateSubTask的同时也启动了线程,无需再单独去调用start()方法来启动线程。
另外30行利用c2引用来获取Coun
ter2类实例中的TextField示例引用,从而改变TextField的显示的手法也是我们需要理解的。
35-76行我们定义了一个Counter2类,这个类和刚才的Counter1类无多大差别——除了37行我们保存了一个需要利用到的SeparateSubTask的引用以
及50-51行我们将这个引用指向了一个新构建的SeparateSubTask类实例(该新线程实例构建即马上运行)。
通过以上代码,我们便可以保证按钮响应顺利的同时,TextField的显示更新同时顺利进行的效果。
当我按下Toggle键时,数字很听话地停在了52.
为什么会这样呢?
思考后我们发现,ui管理的线程和计时更新的线程通过刚才的代码被成功地分配到两个独立的线程中去了——一个是Counter2所在线
程,另一个是SeparateSubTask所在线程——它们两个互不影响,各自做着各自的事情。
另外你会发现,刚才那个代码很好的将界面和功能进行了分模块化构建——较为符合MVC的设计思想。
由于方才的SeparateSubTask和Counter2类之间存在着相互引用的“亲密”关系,所以我们不妨就让它们成为一家子——让SeparateSubTask成为Co
unter2的内部类。
相关代码如下:
Counter2i
SeparateSubTask()
SeparateSubTask();
sp.runFlag
sp.runFlag;
//
invertFlag();
Counter2i();
Counter2i"
这份代码和刚才的代码运行效果是一致的——不信你试试。
需要提醒的是,当两个类之间的耦合关系确实很是紧密的时候,内部类往往是一个不错的选
择。
另外还有另一种形式的处理方法:
将线程类和ui控制类组合到一起。
具体手段则是构建一个Counter3类,让它继承自Applet类并且实现Runable接口
(java中是禁止多继承的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- java 多线程 实例 浅析