BaoYaEr
java线程学习
1. 创建线程
Java的线程继承自Thread的类,处理一个名为run的方法。
class
MyThread
extends
Thread
{
private
int
i
=
0
;
public
void
run()
{
while
(
true
)
{
System.out.println(
++
i);
if
(i
>
100
)
break
;
}
}
}
public
class
program
{
public
static
void
main(String[] args)
{
new
MyThread().start();
}
}
而对于那些不能继承 Thread 的类,可以采取实现 Runnable 接口的方式进行。
2. 执行权转交
对于暂时交出执行权,Java 提供了 Thread.yield() 方法.
class
MyThread
extends
Thread
{
public
void
run()
{
int
i
=
0
;
while
(
true
)
{
System.out.println( getName()
+
"
-----------
"
+
(
++
i));
if
(i
>
100
)
break
;
yield();
}
}
}
public
class
ThreadTest
{
public
static
void
main(String[] args)
{
MyThread t1
=
new
MyThread();
MyThread t2
=
new
MyThread();
t1.setName(
"
t1
"
);
t2.setName(
"
t2
"
);
t1.start();
t2.start();
}
}
结果:
t1-----------1
t2-----------1
t1-----------2
t2-----------2
t1-----------3
t2-----------3
t1-----------4
t2-----------4
t1-----------5
t2-----------5
.....
Java 也提供了 Thread.sleep(); 方法,但由于 Thread.sleep 可能被 interrupt( ) 方法中断,因此必须包含在 try{} 代码块中。
class
MyThread
extends
Thread
{
public
void
run()
{
int
i
=
0
;
while
(
true
)
{
System.out.printf(
"
%s=%d\n
"
, getName(),
++
i);
if
(i
>
100
)
break
;
try
{
sleep(
0
);
}
catch
(InterruptedException e)
{
}
}
}
}
public
class
program
{
public
static
void
main(String[] args)
{
MyThread t1
=
new
MyThread();
MyThread t2
=
new
MyThread();
t1.setName(
"
t1
"
);
t2.setName(
"
t2
"
);
t1.start();
t2.start();
}
}
3. 优先级
Java 使用 setPriority( ) 方法调整优先级。
4. 背景线程
Java 使用 setDaemon() 方法。在 Java 中一般将背景线程称之为"守护线程(daemon thread)"。需要注意的是即便背景线程未结束,进程依然会终止。必须在线程启动前设置。
5. 线程等待
Java 中都使用 join/Join() 方法阻止调用线程,直到某个线程结束。不过 Java 里面情况还是要复杂一些。当某个线程处于 join 等待时,它可能会被 interrupt( ) 方法中断,因此也得放在 try{} 代码块中。
package
tread;
class
ThreadT
extends
Thread
{
public
boolean
stopFlag
=
false
;
public
void
run()
{
int
i
=
0
;
while
(
!
stopFlag)
{
System.out.println(
++
i);
yield();
}
System.out.println(
"
MyThread over
"
);
}
}
class
JoinThread
extends
Thread
{
public
void
run()
{
ThreadT my
=
new
ThreadT();
my.start();
try
{
my.join();
}
catch
(InterruptedException e)
{
my.stopFlag
=
true
;
System.out.println(
"
JoinThread InterruptedException
"
);
}
}
}
public
class
JoinTest
{
public
static
void
main(String[] args)
{
JoinThread join
=
new
JoinThread();
join.start();
try
{
join.sleep(
100
*
5
);
join.interrupt();
}
catch
(InterruptedException e)
{
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:
。。。。。。
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
JoinThread InterruptedException
MyThread over
6. 资源锁定
Java 提供了 synchronized 关键字用来解决多线程资源共享锁定的问题,synchronized 可用于方法或者某个代码段。
package
tread;
class
Res
{
String lock
=
""
;
synchronized
static
void
test()
{
for
(
int
i
=
0
; i
<
10
; i
++
)
{
System.out.println( Thread.currentThread().getName()
+
"
=========
"
+
i);
Thread.yield();
}
}
void
test2()
{
synchronized
(lock)
{
for
(
int
i
=
0
; i
<
10
; i
++
)
{
System.out.println( Thread.currentThread().getName()
+
"
========
"
+
i);
Thread.yield();
//
没有放弃锁
}
}
}
}
class
MyThreadA
extends
Thread
{
public
void
run()
{
new
Res().test2();
}
}
public
class
SynTest
{
public
static
void
main(String[] args)
{
MyThreadA t1
=
new
MyThreadA();
MyThreadA t2
=
new
MyThreadA();
t1.setName(
"
t1
"
);
t2.setName(
"
t2
"
);
t1.start();
t2.start();
}
}
当我们取消 synchronized 关键字时,我们会发现 t1 和 t2 交替执行。添加 synchronized 关键字以后,t2 会在 t1 执行完成后执行,因此对于方法的锁定是成功的。
结果:
t1
========
0
。。。。
t1
========
7
t1
========
8
t1
========
9
t2
========
0
。。。
t2
========
8
t2
========
9
另外,需要注意的是 synchronized 是全局锁定,也就是说对于静态方法而言,它们会锁定全部有此标记的方法(尽管它们不是同一个方法);而对于对象实例,它们会锁定同一对象全部有此标记的方法(尽管它们不是同一个方法)。
7. 原子操作
所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行倒结束,中间不会有任何线程切换操作。
通常所说的Java原子操作包括对非long和double型的primitive进行赋值,以及返回这两者之外的primitive。之所以要把它们排除在外是因为它们都比较大,而JVM的设计规范又没有要求读操作和赋值操作必须是原子操作(JVM可以试着去这么作,但并不保证)。不过如果你在long或double前面加了volatile,那么它就肯定是原子操作了。
看下面的例子,虽然 return i是一个原子操作,但是从获得i的值到方法返回之间有可能被其他线程修改,因此不要主观的认为下面的操作是原子操作。我们还是有必要为其添加同步关键字synchronized。另外 ++i 和 --i 都不是原子操作,它们都涉及读和写两个步骤。
public int getValue() { return i; }
《Thinking in Java》中关于最安全的做法提出了如下的方针,不过我不完全赞同。
如果你要synchronize类的一个方法,索性把所有的方法全都synchronize了。要判断,哪个方法该synchronize,哪个方法可以不synchronize,通常是很难的,而且也没什么把握。
删除synchronized的时候要绝对小心。通常这么做是为了性能,但是synchronized的开销在JDK1.3和1.4里已经大为降低了。此外,只有在用profiler分析过,确认synchronized确实是瓶颈的前提下才能这么作。
我们顺便看看 C# 里面关于原子操作的一些状况。C# 通过 Interlocked 类来提供原子操作的支持,称之为互锁操作。“互锁操作是原子的 — 即整个操作是不能由相同变量上的另一个互锁操作所中断的单元。这在抢先多线程操作系统中是很重要的,在这样的操作系统中,线程可以在从某个内存地址加载值之后但是在有机会更改和存储该值之前被挂起。 ”
8. 线程协同
Java用对象锁来控制多线程安全,而所谓多线程协同无非是利用某种锁机制让线程暂时停顿下来,直到某个“信号”发生。为此,Java 将用来进行多线程协同的方法放在了根 Object 上,包括 wait(); notify(); notifyAll(); 。
调用 wait() / notify() / notifyAll() 之前必须先获取其对象锁,否则会抛出"IllegalMonitorStateException - current thread not owner" 异常。
package
tread;
class
MyThread
extends
Thread
{
static
Object o
=
new
Object();
public
void
run()
{
synchronized
(o)
{
int
i
=
0
;
while
(
++
i
<
10
)
{
System.out.println(i);
try
{
if
(i
==
5
) o.wait();
}
catch
(InterruptedException e)
{
}
}
}
}
}
public
class
SynTest2
{
public
static
void
main(String[] args)
{
MyThread t1
=
new
MyThread();
t1.start();
while
(t1.getState()
!=
Thread.State.WAITING)
{
try
{
Thread.sleep(
100
);
}
catch
(InterruptedException e)
{
}
}
System.out.println(
"
Notify Thread
"
);
synchronized
(MyThread.o)
{
MyThread.o.notify();
}
}
}
输出
1
2
3
4
5
Notify Thread...
6
7
8
9
在上面这个例子中,当 i 等于 5 时,我们调用了对象锁 o 的 wait 方法阻塞线程。在 main 方法中我们循环检查对象状态,并适时发出信号结束等待。这个例子虽然很简单,但是由此可以处理多个线程之间的协同关系。当然,我们还可以给 wait 方法加一个参数,让其等待特定的时间,而不是上面例子中的无限制等待。
需要注意的是 wait() 会释放对象锁,sleep() 则不会。接着看下面的例子。
class MyThread extends Thread {
static Object o = new Object();
public void run() {
synchronized (o) {
int i = 0;
while (++i < 10) {
System.out.printf("%s - %d\n", Thread.currentThread().getName(), i);
try
{
if (i >= 5) o.wait();
}
catch (InterruptedException e)
{
}
}
}
}
}
public class Program {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
输出
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t1 - 5
t2 - 1
t2 - 2
t2 - 3
t2 - 4
t2 - 5
9. 停止/中断线程
线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行。
Java 不推荐使用 stop() / about() / suspend() / resume() 之类的方法来停止线程。原因包括:
在调用这些方法的时候,我们不能确定线程方法执行到何处,是否完成了特定的逻辑。
这些方法会让线程无法正确释放对象锁,可能造成死锁。
推荐的方法:
使用旗标(flag)。
调用 interrupt()。
class MyThread extends Thread {
public boolean stopFlag = false;
public void run() {
while (!stopFlag) {
try
{
System.out.println("Thread running, " + System.currentTimeMillis());
Thread.sleep(50);
}
catch (Exception e)
{
}
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
Thread.sleep(100);
}
catch (Exception e)
{
}
t.stopFlag = true;
}
}
当线程因某种原因处于阻塞等待状态时,我们就无法使用旗标终止它,那么改用interrupt()好了。
class MyThread extends Thread {
public void run() {
try
{
while (true) {
System.out.println("Thread running, " + System.currentTimeMillis());
synchronized (this) {
wait();
}
}
}
catch (Exception e)
{
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
while (t.getState() != Thread.State.WAITING) {
Thread.sleep(500);
}
t.interrupt();
}
catch (Exception e)
{
}
}
}
发表于 2007-04-11 16:24
大田斗
阅读(731)
评论(0)
编辑
收藏
所属分类:
java
新用户注册
刷新评论列表
只有注册用户
登录
后才能发表评论。
网站导航:
博客园
IT新闻
知识库
C++博客
博问
管理
相关文章:
mule事件驱动服务
JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介
在xml的汪洋中遨游之mule篇
linux下java运行脚本
深入浅出之正则表达式【zt】
JavaClassLoader与Package机制
JTA事务初级研究
Spring2.5注释语法
java annotation
J2SE5.0中最有趣的新特性:注释(annotation) [zt]
<
2024年11月
>
日
一
二
三
四
五
六
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
导航
BlogJava
首页
发新随笔
发新文章
联系
聚合
管理
统计
随笔: 32
文章: 427
评论: 144
引用: 0
常用链接
我的随笔
我的评论
我的参与
最新评论
留言簿
(5)
给我留言
查看公开留言
查看私人留言
随笔档案
2008年12月 (1)
2008年4月 (2)
2008年2月 (1)
2008年1月 (1)
2007年12月 (3)
2007年11月 (1)
2007年10月 (3)
2007年7月 (2)
2007年6月 (1)
2007年4月 (2)
2007年3月 (3)
2007年2月 (5)
2007年1月 (3)
2006年12月 (4)
文章分类
axis(6)
(rss)
eclipse(7)
(rss)
Hibernate(30)
(rss)
html/js/css(107)
(rss)
java(106)
(rss)
linux(7)
(rss)
Lucene(7)
(rss)
spring(36)
(rss)
Spring CLOUd(1)
(rss)
Strtus(30)
(rss)
其它(48)
(rss)
开源opensource(48)
(rss)
数据库DateBase(30)
(rss)
设计模式(12)
(rss)
文章档案
2018年8月 (1)
2012年5月 (1)
2012年4月 (2)
2011年7月 (6)
2010年3月 (1)
2010年2月 (1)
2010年1月 (3)
2009年12月 (1)
2009年10月 (1)
2009年8月 (3)
2009年3月 (1)
2009年2月 (1)
2008年12月 (3)
2008年11月 (10)
2008年10月 (3)
2008年9月 (2)
2008年8月 (2)
2008年7月 (4)
2008年6月 (13)
2008年5月 (15)
2008年4月 (9)
2008年3月 (10)
2008年1月 (18)
2007年12月 (33)
2007年11月 (6)
2007年10月 (18)
2007年9月 (10)
2007年8月 (18)
2007年7月 (15)
2007年6月 (25)
2007年5月 (19)
2007年4月 (26)
2007年3月 (38)
2007年2月 (33)
2007年1月 (27)
2006年12月 (27)
2006年11月 (12)
java
Ajax特效网站
cndiy nio
GRO
Hani Suleiman's blog
Java之路
java论坛
J道
mule
mule 入门
oksonic(动画教程)
一路由你
中国eclipse
八进制
在线源码
多线程实战
天火
小米的blogjava
幻境伯克----jface/swt
很全的博克-强
每日一得
满江红
邢红瑞
飞翔
鸟诗选(js)
鸟食轩 (dhtml)
工具
apache中文手册
extjs学习
iconFindre
java 安全
javaresearch
java技巧网
js之王
matrix(study)
prototype api
spring中文
北京IT企业速查
在线流程图工具
雅虎翻译
朋友
Happyshow
hibernate异常
skywalker
sunshow
xf
亚光
同云博客
小弟鹏
张玉磊
昕
李阳
黄鸣
搜索
积分与排名
积分 - 1098441
排名 - 28
最新评论
1. re: hibernate.cfg.xml配置
好全啊 .. 棒棒哒 ~ !
--junqinag.yang
2. re: Quartz任务调度快速入门
我现在来看还是觉得不错
--小任
3. re: js中this的总结
评论内容较长,点击标题查看
--pam
4. re: Quartz任务调度快速入门
楼主辛苦
--yd
5. re: Quartz任务调度快速入门
顶了,内容写的很好
--sen
阅读排行榜
1. 网页不缓存(3536)
2. Form嵌套引起的问题 (2830)
3. 解决IE下CSS背景图片闪烁的Bug(2432)
4. Spring AOP的动态载入原理(2394)
5. 如何制作漂亮的Excel表格(2022)
评论排行榜
1. 北京户口--吃官司(5)
2. 开始→运行→输入的命令集锦(3)
3. 让网页上的所有图片动起来(2)
4. Dom4j 编码问题彻底解决 (1)
5. 心情不爽(1)