Java线程总结(转)
上一篇 / 下一篇 2008-10-21 16:43:23 / 个人分类:JAVA类
51Testing软件测试网*fQGtkZ {~M
51Testing软件测试网%j'fYF~;|
7rk(B,Z6N0在论坛上面常常看到初学者对线程的无可奈何,所以总结出了下面一篇文章,希望对一些正在学习使用java线程的初学者有所帮助。
:h/B2q;M\7w0_051Testing软件测试网sN
t"F4~9O
首先要理解线程首先需要了解一些基本的东西,我们现在所使用的大多数操作系统都属于多任务,分时操作系统。正是由于这种操作系统的出现才有了多线程这个概念。我们使用的windows,linux就属于此列。什么是分时操作系统呢,通俗一点与就是可以同一时间执行多个程序的操作系统,在自己的电脑上面,你是不是一边听歌,一边聊天还一边看网页呢?但实际上,并不上cpu在同时执行这些程序,cpu只是将时间切割为时间片,然后将时间片分配给这些程序,获得时间片的程序开始执行,不等执行完毕,下个程序又获得时间片开始执行,这样多个程序轮流执行一段时间,由于现在cpu的高速计算能力,给人的感觉就像是多个程序在同时执行一样。51Testing软件测试网8S x k9J8V'm!G
一般可以在同一时间内执行多个程序的操作系统都有进程的概念.一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间,一组系统资源.在进程概念中,每一个进程的内部数据和状态都是完全独立的.因此可以想像创建并执行一个进程的系统开像是比较大的,所以线程出现了。在java中,程序通过流控制来执行程序流,程序中单个顺序的流控制称为线程,多线程则指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务.多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行.(你可以将前面一句话的程序换成进程,进程是程序的一次执行过程,是系统运行程序的基本单位)51Testing软件测试网 ON h)]|FU1o
8g%uI)H,j.x!yU0线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈.所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程也被称为轻负荷进程(light-weight process).一个进程中可以包含多个线程.
(bLEeBwxn051Testing软件测试网hb2u2F#l;s aA
多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程,同进程一样,一个线程也有从创建,运行到消亡的过程,称为线程的生命周期.用线程的状态(state)表明线程处在生命周期的哪个阶段.线程有创建,可运行,运行中,阻塞,死亡五中状态.通过线程的控制与调度可使线程在这几种状态间转化每个程序至少自动拥有一个线程,称为主线程.当程序加载到内存时,启动主线程.51Testing软件测试网8]M/pdO~ `
51Testing软件测试网9Y%zw]6`v
[线程的运行机制以及调度模型]
d3YAX{
g'k!P:T$z0java中多线程就是一个类或一个程序执行或管理多个线程执行任务的能力,每个线程可以独立于其他线程而独立运行,当然也可以和其他线程协同运行,一个类控制着它的所有线程,可以决定哪个线程得到优先级,哪个线程可以访问其他类的资源,哪个线程开始执行,哪个保持休眠状态。
@{ zZ} J`0下面是线程的机制图:51Testing软件测试网P4V3h^ew!o,Gxv P
tF(oVouGZ%c0线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务.线程有创建,可运行,运行中,阻塞,死亡五中状态.一个具有生命的线程,总是处于这五种状态之一:
Sg0jZ0J.zmC
E01.创建状态51Testing软件测试网O.v-Q(S+[;ygd7Z
使用new运算符创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程处于创建状态(new thread)
MMgC)z^k02.可运行状态
&h2J8PM|-C0使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于可运行状态(Runnable)
i"Z&h
ZJC@03.运行中状态51Testing软件测试网(EF%D-Rp;W3\?
Java运行系统通过调度选中一个Runnable的线程,使其占有CPU并转为运行中状态(Running).此时,系统真正执行线程的run()方法.
'T6d)E)d,g-opK(BOo6k04.阻塞状态51Testing软件测试网x[^r"o
一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态(Blocked)
m^HE_DB05.死亡状态
oP/r+U%q9q ~-{9c+t0线程结束后是死亡状态(Dead)
]f[%OIqFa%S051Testing软件测试网l[h ~S(^
同一时刻如果有多个线程处于可运行状态,则他们需要排队等待CPU资源.此时每个线程自动获得一个线程的优先级(priority),优先级的高低反映线程的重要或紧急程度.可运行状态的线程按优先级排队,线程调度依据优先级基础上的"先到先服务"原则.
G0tH+M] yOG[0线程调度管理器负责线程排队和CPU在线程间的分配,并由线程调度算法进行调度.当线程调度管理器选种某个线程时,该线程获得CPU资源而进入运行状态.51Testing软件测试网*p @|]*l5CUP*LD
Pk ?L'e1I2BEi2J0线程调度是先占式调度,即如果在当前线程执行过程中一个更高优先级的线程进入可运行状态,则这个线程立即被调度执行.先占式调度分为:独占式和分时方式.51Testing软件测试网;Ftc!["vG6g
独占方式下,当前执行线程将一直执行下去,直 到执行完毕或由于某种原因主动放弃CPU,或CPU被一个更高优先级的线程抢占
Q}Q3|t0分时方式下,当前运行线程获得一个时间片,时间到时,即使没有执行完也要让出CPU,进入可运行状态,等待下一个时间片的调度.系统选中其他可运行状态的线程执行51Testing软件测试网 d[ MbY8Gv
分时方式的系统使每个线程工作若干步,实现多线程同时运行51Testing软件测试网Vw/x,FYp{o
#?KEV'W,eB%U0另外请注意下面的线程调度规则(如果有不理解,不急,往下看):51Testing软件测试网/M:nb3Xic2O
①如果两个或是两个以上的线程都修改一个对象,那么把执行修改的方法定义为被同步的(Synchronized),如果对象更新影响到只读方法,那么只度方法也应该定义为同步的51Testing软件测试网.~ K7WI%f1W'[q#R"P6N}A
②如果一个线程必须等待一个对象状态发生变化,那么它应该在对象内部等待,而不是在外部等待,它可以调用一个被同步的方法,并让这个方法调用wait()51Testing软件测试网A3B7?2c]Z&H
j(l$I
③每当一个方法改变某个对象的状态的时候,它应该调用notifyAll()方法,这给等待队列的线程提供机会来看一看执行环境是否已发生改变51Testing软件测试网iFn?(Kk;f#L*i
④记住wait(),notify(),notifyAll()方法属于Object类,而不是Thread类,仔细检查看是否每次执行wait()方法都有相应的notify()或notifyAll()方法,且它们作用与相同的对象 在java中每个类都有一个主线程,要执行一个程序,那么这个类当中一定要有main方法,这个man方法也就是java class中的主线程。你可以自己创建线程,有两种方法,一是继承Thread类,或是实现Runnable接口。一般情况下,最好避免继承,因为java中是单根继承,如果你选用继承,那么你的类就失去了弹性,当然也不能全然否定继承Thread,该方法编写简单,可以直接操作线程,适用于单重继承情况。至于选用那一种,具体情况具体分析。51Testing软件测试网,x5m9lT/Da
u }
Pzt-s$u7X6o051Testing软件测试网:H.prgGD
Q{
eg.继承Thread
51Testing软件测试网)ys ^I [/}
public class MyThread_1 extends Thread51Testing软件测试网3Q;ZOT$ZEH&A"]
{
`gS$}-z}zL0public void run()51Testing软件测试网4T7X!Qfd0L:yjJT
{51Testing软件测试网 }LOpQLFk[Rs
//some code51Testing软件测试网/x7Z3x0l3K9q-],g,P
}
[2R:P,@R _c q.]o"a0}
51Testing软件测试网3N(u/J7r T'Yws/D
4eK3JH,S(L{0
N}0cum o%h)G0eg.实现Runnable接口
5llV D?`K0
+OI%exsjYhzL0
public class MyThread_2 implements Runnable
R2p[v'Z*c
O^ku0{51Testing软件测试网nf*l0[gb
public void run()51Testing软件测试网5Mb;q(Z~;] }9{7xG]H
{51Testing软件测试网T)Ua"_V K
//some code
!a|VI9sJsu0}51Testing软件测试网1}XS(j0k^.}
}
51Testing软件测试网2g]xZN|\,K
;W3@7i-i C.L051Testing软件测试网,ae-J9x"apjD#W;I
8PA#a%Eb"i)JD0当使用继承创建线程,这样启动线程:
51Testing软件测试网o+tO@f*F
new MyThread_1().start()
51Testing软件测试网qN~ r}f/[@5uq
7j4v3|wA%vB051Testing软件测试网k wcO qRI?9u'A
当使用实现接口创建线程,这样启动线程:
5L,f1tc_3p"fa W051Testing软件测试网V o pG tfk4p*l,U[ A
new Thread(new MyThread_2()).start()
g+M-P"r,M,U~OO0
;Ml4U*d.L qs%\p3T0
51Testing软件测试网|1wnF9e M
51Testing软件测试网:fNuH$y8qq
注意,其实是创建一个线程实例,并以实现了Runnable接口的类为参数传入这个实例,当执行这个线程的时候,MyThread_2中run里面的代码将被执行。
Kq jFIoFKe0下面是完成的例子:
z"?:Z#vRm7zTn M B}0
public class MyThread implements Runnable
IH2z~3k,E c0{51Testing软件测试网d
T-Rf)h#X{+LN+IA
_n6|s j-Q|-{z] _S0public void run()51Testing软件测试网.iy?#{Wpz6S
{
]a!wH;B0System.out.println("My Name is "+Thread.currentThread().getName());51Testing软件测试网&\c*N1]i#R|#E+W
}
7jv*Bu7Jy2}8f&o8K0public static void main(String[] args)51Testing软件测试网,d"W+s Vd"B/D
{51Testing软件测试网+Ye[h'}n.^8x
new Thread(new MyThread()).start();51Testing软件测试网+RH6PLX5D;t6`y.x
}
ig&Y,o9vswjQ0}
WK;lhm b6Oiq#k0
'K5n:d UmE-g+[0
51Testing软件测试网%T%E$NON7E(|6X*j
9X"GIU-vJ)qCz0
u8AGt] tu0执行后将打印出:
0nEi Ou%F4m0My Name is Thread-0
f-wH4f{0
6P!MX{7[$x0你也可以创建多个线程,像下面这样
51Testing软件测试网Fz4mE^?/m
new Thread(new MyThread()).start();51Testing软件测试网
xc@1s a4n
new Thread(new MyThread()).start();51Testing软件测试网A jEDisB
new Thread(new MyThread()).start();
.k]Y T@051Testing软件测试网 G3ZA+i+?B"J B PR`
51Testing软件测试网*PG%^2Xm,D`
hX6JW6O6t0
r6_0[J@Y0那么会打印出:51Testing软件测试网3A"k-I-Z2M7g#zJn
My Name is Thread-051Testing软件测试网4t Wwy2_%m
My Name is Thread-1
6L;i#^(?
Fw7U\0My Name is Thread-251Testing软件测试网x}yOeB
51Testing软件测试网+f&S9SG%N
X#A:t
看了上面的结果,你可能会认为线程的执行顺序是依次执行的,但是那只是一般情况,千万不要用以为是线程的执行机制;影响线程执行顺序的因素有几点:首先看看前面提到的优先级别
;vo2n T@X%u0
public class MyThread implements Runnable
6P*bid/X0{51Testing软件测试网$q^3e:H%P0f(|;|
2zZ,zt
r&bY[1zy0public void run()51Testing软件测试网N-PC1u{u
uL
{
kA7W(wI0System.out.println("My Name is "+Thread.currentThread().getName());
3M5^U)G"\(WK0}51Testing软件测试网4D_.|9xV+d
public static void main(String[] args)51Testing软件测试网'b;o&S5Tfh9d;Z,Z
{
A%Z"\Z:Z/kf D0Thread t1=new Thread(new MyThread());
'yyo`E"eZK+~a0Thread t2=new Thread(new MyThread());
c8h,]P
wB0Thread t3=new Thread(new MyThread());
`{/PP([ }e/l0t2.setPriority(Thread.MAX_PRIORITY);//赋予最高优先级
9m
Z g
S&zh0Mh0t1.start();51Testing软件测试网#T&W,c;iu^#}O)X
t2.start();
3Q6iy6L&K\7\@0t3.start();51Testing软件测试网(z1i9Z.T*D*tT
R1`
}51Testing软件测试网_8D,KG(e_
}
2{GyK0U3rj0
$h{2r#y#l?0
BP stZ7MM
X0
%Av)ncCM0再看看结果:51Testing软件测试网5iq!v0a Z7v9f\!Il
My Name is Thread-1
^i J F/l8e0My Name is Thread-0
,Ox8Nv$]8M0My Name is Thread-251Testing软件测试网| W,L9}
d'p7S
0n}Y[+B1lg8U%]0
/_~2x']0La+i]0线程的优先级分为10级,分别用1到10的整数代表,默认情况是5。上面的t2.setPriority(Thread.MAX_PRIORITY)等价与t2.setPriority(10)
'N:L7K;|~1f6t.i0然后是线程程序本身的设计,比如使用sleep,yield,join,wait等方法(详情请看JDKDocument)
L XUWea X0
On8Uq(y+@0
public class MyThread implements Runnable
8ly8C%VbMI9p0{
8gM'D['r1t;y
\8x(Q0public void run()51Testing软件测试网 Ad
T!U2q:m_K-U
{51Testing软件测试网G's(Y(dK0W9fm A*}
try
J5O-HQqr/@:g0{51Testing软件测试网yP#p7`'t/dk4w)O+|fn
int sleepTime=(int)(Math.random()*100);//产生随机数字,51Testing软件测试网L
Fwj!@i;YF5\
Thread.currentThread().sleep(sleepTime);//让其休眠一定时间,时间又上面sleepTime决定51Testing软件测试网]-Oh?,r7^
//public static void sleep(long millis)throw InterruptedException(API)
5R};Syt9g[0System.out.println(Thread.currentThread().getName()+"睡了"+sleepTime);51Testing软件测试网-^{*G
]z\,e
}catch(InterruptedException ie)//由于线程在休眠可能被中断,所以调用sleep方法的时候需要捕捉异常
\q
[A t-M0{
L:Z#\J/aIm'AWn0ie.printStackTrace();51Testing软件测试网A%Lu9IE
rh*CB&[L c1W5I
}
&qAR*\O1r0}51Testing软件测试网d&A
q)g2BG:O"a
public static void main(String[] args)
C
Y/L"D
t0{
'S_C6m%{[:rD0Thread t1=new Thread(new MyThread());51Testing软件测试网 M|ZaRid
Thread t2=new Thread(new MyThread());51Testing软件测试网 ~u'w9E"pi
Thread t3=new Thread(new MyThread());
.cg
mL'rI0t1.start();51Testing软件测试网n0{ t blbn&q
t2.start();51Testing软件测试网~ `Q5D8Y+} Bp:T
t3.start();
XjUSgu0}51Testing软件测试网7{Bb!Y({`y,k&Wa
}
/B B8}:zsw/B K0
4u5}$~#@+A6g2aG_0
OD h h7Bz
]@051Testing软件测试网p9Ko [7[xU
执行后观察其输出:51Testing软件测试网"d(} C/s'|Qy
I*ydmfp0Thread-0睡了11
,n]e
_!ON,U/l"N0Thread-2睡了48
Nu*B+q8_"A0Thread-1睡了69
A'QIR%ET9d0
3vDeAW2fvj|0
F4x }"hA9PM9?+N051Testing软件测试网,a'BkP'{M+W._z
上面的执行结果是随机的,再执行很可能出现不同的结果。由于上面我在run中添加了休眠语句,当线程休眠的时候就会让出cpu,cpu将会选择执行处于runnable状态中的其他线程,当然也可能出现这种情况,休眠的Thread立即进入了runnable状态,cpu再次执行它。
0R(\$},^)V8bp%U.@1@kl0[线程组概念]
oWdff!d0线程是可以被组织的,java中存在线程组的概念,每个线程都是一个线程组的成员,线程组把多个线程集成为一个对象,通过线程组可以同时对其中的多个线程进行操作,如启动一个线程组的所有线程等.Java的线程组由java.lang包中的Thread——Group类实现.51Testing软件测试网2H{;i-?0_&kq
ThreadGroup类用来管理一组线程,包括:线程的数目,线程间的关系,线程正在执行的操作,以及线程将要启动或终止时间等.线程组还可以包含线程组.在Java的应用程序中,最高层的线程组是名位main的线程组,在main中还可以加入线程或线程组,在mian的子线程组中也可以加入线程和线程组,形成线程组和线程之间的树状继承关系。像上面创建的线程都是属于main这个线程组的。
&Mo1CT,DZ0借用上面的例子,main里面可以这样写:
xR_1~(}9z051Testing软件测试网j`8Q4t:qjl
public static void main(String[] args)51Testing软件测试网wKk5Iv
{51Testing软件测试网hG"r6o.a/z;x$j
/***************************************
y)z)p;r,Kg0ThreadGroup(String name)
/K1_5[;A|#Q H0ThreadGroup(ThreadGroup parent, String name)51Testing软件测试网%~J I/]C~
***********************************/51Testing软件测试网"I-pN.Fw^1jh
ThreadGroup group1=new ThreadGroup("group1");
4['s
}1h/as0JKwz0ThreadGroup group2=new ThreadGroup(group1,"group2");51Testing软件测试网(M+h8x?P^3xdg c
Thread t1=new Thread(group2,new MyThread());51Testing软件测试网2cb#[i~4Mn
Thread t2=new Thread(group2,new MyThread());51Testing软件测试网*TF"p)I/V0K
Thread t3=new Thread(group2,new MyThread());51Testing软件测试网q:jh$@MLb8j
t1.start();51Testing软件测试网qNt-f w4{F
Nkl)e-Z
t2.start();51Testing软件测试网6k e8i\P/_3MHU
t3.start();51Testing软件测试网0^3[PkDa
}
/W$pY,A~051Testing软件测试网z!{WKT#XG
%@i6xrd.z"JK0
]%ann`!}0Lji0
olx`z
^ ?0线程组的嵌套,t1,t2,t3被加入group2,group2加入group1。51Testing软件测试网7Z-`+];|\-L6nC
另外一个比较多就是关于线程同步方面的,试想这样一种情况,你有一笔存款在银行,你在一家银行为你的账户存款,而你的妻子在另一家银行从这个账户提款,现在你有1000块在你的账户里面。你存入了1000,但是由于另一方也在对这笔存款进行操作,人家开始执行的时候只看到账户里面原来的1000元,当你的妻子提款1000元后,你妻子所在的银行就认为你的账户里面没有钱了,而你所在的银行却认为你还有2000元。51Testing软件测试网%q0ye\\ D/K
看看下面的例子:
aM,L1uJ051Testing软件测试网W$\S)v;H&?
class BlankSaving //储蓄账户51Testing软件测试网K,f"N`'~S
{51Testing软件测试网B#G#xhe
private static int money=10000;
z*jB ~C0public void add(int i)
XS8H3P?"]0{