- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Java线程的生命周期和状态控制_动力节点Java学院整理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
1、线程的生命周期 。
线程状态转换图:
1、新建状态 。
用new关键字和thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable).
注意:不能对已经启动的线程再次调用start()方法,否则会出现javalang.illegalthreadstateexception异常.
2、就绪状态 。
处于就绪状态的线程已经具备了运行条件,但还没有分配到cpu,处于线程就绪队列(尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。因为cpu的调度不一定是按照先进先出的顺序来调度的),等待系统为其分配cpu。等待状态并不是执行状态,当系统选定一个等待执行的thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得cpu,线程就进入运行状态并自动调用自己的run方法.
提示:如果希望子线程调用start()方法后立即执行,可以使用thread.sleep()方式使主线程睡眠一伙儿,转去执行子线程.
3、运行状态 。
处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。处于就绪状态的线程,如果获得了cpu的调度,就会从就绪状态变为运行状态,执行run()方法中的任务。如果该线程失去了cpu资源,就会又从运行状态变为就绪状态。重新等待系统分配资源。也可以对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态.
当发生如下情况是,线程会从运行状态变为阻塞状态:
①、线程调用sleep方法主动放弃所占用的系统资源 。
②、线程调用一个阻塞式io方法,在该方法返回之前,该线程被阻塞 。
③、线程试图获得一个同步监视器,但更改同步监视器正被其他线程所持有 。
④、线程在等待某个通知(notify) 。
⑤、程序调用了线程的suspend方法将线程挂起。不过该方法容易导致死锁,所以程序应该尽量避免使用该方法.
当线程的run()方法执行完,或者被强制性地终止,例如出现异常,或者调用了stop()、desyory()方法等等,就会从运行状态转变为死亡状态.
4、阻塞状态 。
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待i/o设备等资源,将让出cpu并暂时停止自己的运行,进入阻塞状态。 在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的i/o设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行.
5、死亡状态 。
当线程的run()方法执行完,或者被强制性地终止,就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.illegalthreadstateexception异常.
2、线程状态的控制 。
java提供了一些便捷的方法用于会线程状态的控制.
. 。
可以看到很多方法,已经标注为过时的,我们应该尽可能的避免使用它们,而应该重点关注start()、interrupt()、join()、sleep()、yield()等直接控制方法,和setdaemon()、setpriority()等间接控制方法.
1、线程睡眠——sleep 。
如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用thread的sleep方法,从上面可以看到sleep方法有两种重载的形式,但是使用方法一样.
比如,我们想要使主线程每休眠100毫秒,然后再打印出数字:
1
2
3
4
5
6
7
8
|
public
class
test {
public
static
void
main(string[] args)
throws
interruptedexception {
for
(
int
i=;i<;i++){
system.out.println(
"main"
+i);
thread.sleep();
}
}
}
|
可以明显看到打印的数字在时间上有些许的间隔.
注意如下几点问题 。
①、sleep是静态方法,最好不要用thread的实例对象调用它,因为它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效。看下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
test1 {
public
static
void
main(string[] args)
throws
interruptedexception {
system.out.println(thread.currentthread().getname());
mythread mythread=
new
mythread();
mythread.start();
mythread.sleep(
1000
);
//这里sleep的就是main线程,而非mythread线程
thread.sleep(
10
);
for
(
int
i=
0
;i<
100
;i++){
system.out.println(
"main"
+i);
}
}
}
|
②、java线程调度是java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。但是不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。因为使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
class
test1 {
public
static
void
main(string[] args)
throws
interruptedexception {
new
mythread().start();
new
mythread().start();
}
}
class
mythread
extends
thread {
@override
public
void
run() {
for
(
int
i =
0
; i <
3
; i++) {
system.out.println(
this
.getname()+
"线程"
+ i +
"次执行!"
);
try
{
thread.sleep(
50
);
}
catch
(interruptedexception e) {
e.printstacktrace();
}
}
}
}
|
看某一次的运行结果:
1. thread-0线程0次执行! 2. thread-1线程0次执行! 3. thread-1线程1次执行! 4. thread-0线程1次执行! 5. thread-0线程2次执行! 6. thread-1线程2次执行! 。
可以看到,线程0首先执行,然后线程1执行一次,又了执行一次。可以看到它并不是按照sleep的顺序执行的.
2、线程让步——yield 。
yield()方法和sleep()方法有点相似,它也是thread类提供的一个静态的方法,它也可以让当前正在执行的线程暂停,让出cpu资源给其他的线程。但是和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态。yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,完全可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行.
实际上,当某个线程调用了yield()方法暂停之后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能获得执行的机会,当然,只是有可能,因为我们不可能精确的干涉cpu调度线程.
yield的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
test1 {
public
static
void
main(string[] args)
throws
interruptedexception {
new
mythread(
"低级"
,
1
).start();
new
mythread(
"中级"
,
5
).start();
new
mythread(
"高级"
,
10
).start();
}
}
class
mythread
extends
thread {
public
mythread(string name,
int
pro) {
super
(name);
// 设置线程的名称
this
.setpriority(pro);
// 设置优先级
}
@override
public
void
run() {
for
(
int
i =
0
; i <
30
; i++) {
system.out.println(
this
.getname() +
"线程第"
+ i +
"次执行!"
);
if
(i %
5
==
0
)
thread.yield();
}
}
}
|
关于sleep()方法和yield()方的区别如下:
①、sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态.
②、sleep方法声明抛出了interruptedexception,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常.
③、sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行.
3、线程合并——join 。
线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时,thread类提供了join方法来完成这个功能,注意,它不是静态方法.
从上面的方法的列表可以看到,它有3个重载的方法:
void join() 。
当前线程等该加入该线程后面,等待该线程终止.
void join(long millis) 。
当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度 。
void join(long millis,int nanos) 。
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度 。
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
test1 {
public
static
void
main(string[] args)
throws
interruptedexception {
mythread thread=
new
mythread();
thread.start();
thread.join(
1
);
//将主线程加入到子线程后面,不过如果子线程在1毫秒时间内没执行完,则主线程便不再等待它执行完,进入就绪状态,等待cpu调度
for
(
int
i=
0
;i<
30
;i++){
system.out.println(thread.currentthread().getname() +
"线程第"
+ i +
"次执行!"
);
}
}
}
class
mythread
extends
thread {
@override
public
void
run() {
for
(
int
i =
0
; i <
1000
; i++) {
system.out.println(
this
.getname() +
"线程第"
+ i +
"次执行!"
);
}
}
}
|
在这个例子中,在主线程中调用thread.join(); 就是将主线程加入到thread子线程后面等待执行。不过有时间限制,为1毫秒.
4、线程的优先级 。
每个线程执行时都有一个优先级的属性,优先级高的线程可以获得较多的执行机会,而优先级低的线程则获得较少的执行机会。与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取cpu资源的概率较大,优先级低的也并非没机会执行。 每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级.
thread类提供了setpriority(int newpriority)和getpriority()方法来设置和返回一个指定线程的优先级,其中setpriority方法的参数是一个整数,范围是1~•0之间,也可以使用thread类提供的三个静态常量:
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
test1 {
public
static
void
main(string[] args)
throws
interruptedexception {
new
mythread(
"高级"
,
10
).start();
new
mythread(
"低级"
,
1
).start();
}
}
class
mythread
extends
thread {
public
mythread(string name,
int
pro) {
super
(name);
//设置线程的名称
setpriority(pro);
//设置线程的优先级
}
@override
public
void
run() {
for
(
int
i =
0
; i <
100
; i++) {
system.out.println(
this
.getname() +
"线程第"
+ i +
"次执行!"
);
}
}
}
|
从结果可以看到 ,一般情况下,高级线程更显执行完毕.
注意一点:虽然java提供了10个优先级别,但这些优先级别需要操作系统的支持。不同的操作系统的优先级并不相同,而且也不能很好的和java的10个优先级别对应。所以我们应该使用max_priority、min_priority和norm_priority三个静态常量来设定优先级,这样才能保证程序最好的可移植性.
5、守护线程 。
守护线程与普通线程写法上基本么啥区别,调用线程对象的方法setdaemon(true),则可以将其设置为守护线程。 。
守护线程使用的情况较少,但并非无用,举例来说,jvm的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。 。
setdaemon方法的详细说明:
public final void setdaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,java 虚拟机退出。 。
该方法必须在启动线程前调用。 该方法首先调用该线程的 checkaccess 方法,且不带任何参数。这可能抛出 securityexception(在当前线程中)。 。
参数:
on - 如果为 true,则将该线程标记为守护线程。 。
抛出: 。
illegalthreadstateexception - 如果该线程处于活动状态。 。
securityexception - 如果当前线程无法修改该线程.
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
31
32
33
34
35
36
|
/**
* java线程:线程的调度-守护线程
*/
public
class
test {
public
static
void
main(string[] args) {
thread t1 =
new
mycommon();
thread t2 =
new
thread(
new
mydaemon());
t2.setdaemon(
true
);
//设置为守护线程
t2.start();
t1.start();
}
}
class
mycommon
extends
thread {
public
void
run() {
for
(
int
i =
0
; i <
5
; i++) {
system.out.println(
"线程1第"
+ i +
"次执行!"
);
try
{
thread.sleep(
7
);
}
catch
(interruptedexception e) {
e.printstacktrace();
}
}
}
}
class
mydaemon
implements
runnable {
public
void
run() {
for
(
long
i =
0
; i < 9999999l; i++) {
system.out.println(
"后台线程第"
+ i +
"次执行!"
);
try
{
thread.sleep(
7
);
}
catch
(interruptedexception e) {
e.printstacktrace();
}
}
}
}
|
执行结果:
1. 后台线程第0次执行! 2. 线程1第0次执行! 3. 线程1第1次执行! 4. 后台线程第1次执行! 5. 后台线程第2次执行! 6. 线程1第2次执行! 7. 线程1第3次执行! 8. 后台线程第3次执行! 9. 线程1第4次执行! 10. 后台线程第4次执行! 11. 后台线程第5次执行! 12. 后台线程第6次执行! 13. 后台线程第7次执行! 。
从上面的执行结果可以看出:前台线程是保证执行完毕的,后台线程还没有执行完毕就退出了。 。
实际上:jre判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台县城时候一定要注意这个问题.
守护线程的用途:
守护线程通常用于执行一些后台作业,例如在你的应用程序运行时播放背景音乐,在文字编辑器里做自动语法检查、自动保存等功能。java的垃圾回收也是一个守护线程。守护线的好处就是你不需要关心它的结束问题。例如你在你的应用程序运行的时候希望播放背景音乐,如果将这个播放背景音乐的线程设定为非守护线程,那么在用户请求退出的时候,不仅要退出主线程,还要通知播放背景音乐的线程退出;如果设定为守护线程则不需要了.
6、如何结束一个线程 。
thread.stop()、thread.suspend、thread.resume、runtime.runfinalizersonexit这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的!想要安全有效的结束一个线程,可以使用下面的方法.
1、正常执行完run方法,然后结束掉 。
2、控制循环条件和判断条件的标识符来结束掉线程 。
比如说run方法这样写:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
mythread
extends
thread {
int
i=
0
;
@override
public
void
run() {
while
(
true
) {
if
(i==
10
)
break
;
i++;
system.out.println(i);
}
}
}
|
或者 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
mythread
extends
thread {
int
i=
0
;
boolean
next=
true
;
@override
public
void
run() {
while
(next) {
if
(i==
10
)
next=
false
;
i++;
system.out.println(i);
}
}
}
|
或者 。
1
2
3
4
5
6
7
8
9
10
11
12
|
class
mythread
extends
thread {
int
i=
0
;
@override
public
void
run() {
while
(
true
) {
if
(i==
10
)
return
;
i++;
system.out.println(i);
}
}
}
|
只要保证在一定的情况下,run方法能够执行完毕即可。而不是while(true)的无线循环.
3、使用interrupt结束一个线程.
诚然,使用第2中方法的标识符来结束一个线程,是一个不错的方法,但是如果,该线程是处于sleep、wait、join的状态的时候,while循环就不会执行,那么我们的标识符就无用武之地了,当然也不能再通过它来结束处于这3种状态的线程了.
可以使用interrupt这个巧妙的方式结束掉这个线程.
我们看看sleep、wait、join方法的声明:
1
2
3
|
public
final
void
wait()
throws
interruptedexception
public
static
native
void
sleep(
long
millis)
throws
interruptedexception
public
final
void
join()
throws
interruptedexception
|
可以看到,这三者有一个共同点,都抛出了一个interruptedexception的异常.
在什么时候会产生这样一个异常呢?
每个thread都有一个中断状状态,默认为false。可以通过thread对象的isinterrupted()方法来判断该线程的中断状态。可以通过thread对象的interrupt()方法将中断状态设置为true.
当一个线程处于sleep、wait、join这三种状态之一的时候,如果此时他的中断状态为true,那么它就会抛出一个interruptedexception的异常,并将中断状态重新设置为false.
看下面的简单的例子:
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
|
public
class
test1 {
public
static
void
main(string[] args)
throws
interruptedexception {
mythread thread=
new
mythread();
thread.start();
}
}
class
mythread
extends
thread {
int
i=
1
;
@override
public
void
run() {
while
(
true
) {
system.out.println(i);
system.out.println(
this
.isinterrupted());
try
{
system.out.println(
"我马上去sleep了"
);
thread.sleep(
2000
);
this
.interrupt();
}
catch
(interruptedexception e) {
system.out.println(
"异常捕获了"
+
this
.isinterrupted());
return
;
}
i++;
}
}
}
|
测试结果:
1. 1 2. false 3. 我马上去sleep了 4. 2 5. true 6. 我马上去sleep了 7. 异常捕获了false 。
可以看到,首先执行第一次while循环,在第一次循环中,睡眠2秒,然后将中断状态设置为true。当进入到第二次循环的时候,中断状态就是第一次设置的true,当它再次进入sleep的时候,马上就抛出了interruptedexception异常,然后被我们捕获了。然后中断状态又被重新自动设置为false了(从最后一条输出可以看出来).
所以,我们可以使用interrupt方法结束一个线程。具体使用如下:
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
|
public
class
test1 {
public
static
void
main(string[] args)
throws
interruptedexception {
mythread thread=
new
mythread();
thread.start();
thread.sleep(
3000
);
thread.interrupt();
}
}
class
mythread
extends
thread {
int
i=
0
;
@override
public
void
run() {
while
(
true
) {
system.out.println(i);
try
{
thread.sleep(
1000
);
}
catch
(interruptedexception e) {
system.out.println(
"中断异常被捕获了"
);
return
;
}
i++;
}
}
}
|
多测试几次,会发现一般有两种执行结果:
0 1 2 。
4. 中断异常被捕获了 。
或者 。
0 1 2 3 。
5. 中断异常被捕获了 。
这两种结果恰恰说明了 只要一个线程的中断状态一旦为true,只要它进入sleep等状态,或者处于sleep状态,立马回抛出interruptedexception异常.
第一种情况,是当主线程从3秒睡眠状态醒来之后,调用了子线程的interrupt方法,此时子线程正处于sleep状态,立马抛出interruptedexception异常.
第一种情况,是当主线程从3秒睡眠状态醒来之后,调用了子线程的interrupt方法,此时子线程还没有处于sleep状态。然后再第3次while循环的时候,在此进入sleep状态,立马抛出interruptedexception异常.
最后此篇关于Java线程的生命周期和状态控制_动力节点Java学院整理的文章就讲到这里了,如果你想了解更多关于Java线程的生命周期和状态控制_动力节点Java学院整理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
已关闭。这个问题是 off-topic 。目前不接受答案。 想要改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 已关闭10 年前。 Improve th
我正在尝试将 JSON 发送到我的服务器并作为结果检索 JSON。例如发送用户名和密码并取回 token 和其他内容。 这就是我正在为发送的 HTTP 请求所做的。我现在如何检索同一请求中的内容?
我有以下 xts 矩阵: > options(digits.secs = 6) > set.seed(1234) > xts(1:10, as.POSIXlt(1366039619, tz="EST"
我目前正在开发一个应用程序,当用户到达某个位置时,它会提醒用户。我希望这个应用程序也在后台运行并搜索解决方案。 在 AppStore 中,我发现了一款名为“Sleep Cycle”的应用程序,它可
我想创建一个基于 farbtastic color picker 的颜色选择器。我想要实现的是添加我想要链接到色轮的 RGB slider 。这是我到目前为止所拥有的。 app.controller(
RFC 5545 允许 RDATE 属性具有 PERIOD 数据类型。该数据类型的语义是什么?据我所知,这是未指定的。它会改变事件的持续时间吗?如果时区更改且没有持续时间怎么办? 最佳答案 尽管我
在 CodinGame学习平台,C# 教程中用作示例的问题之一是: The aim of this exercise is to check the presence of a number in a
我听说网上有一本英特尔书,它描述了特定汇编指令所需的 CPU 周期,但我找不到(经过努力)。谁能告诉我如何找到CPU周期? 这是一个例子,在下面的代码中,mov/lock 是 1 个 CPU 周期,x
据我所知,Java GC有次要GC(低成本)和主要GC周期(高成本)。如果对象在本地范围内,则会在 Minor GC 中清理它。如果对象的引用存储在代码中的其他位置,则它会在主 GC 中被清除。 例如
到目前为止,我有一个很好的自旋锁,可以用作 intendend: std::atomic_flag barrier = ATOMIC_FLAG_INIT; inline void lo
晚上好,我将 cycle2 与 prev 和 next 函数一起使用,但我无法将 prev 和 next 函数置于图像下方的中心。我环顾四周,我知道这会很愚蠢,但我就是看不到它。非常令人沮丧。谢谢加里
出于教育目的,我想知道在优化(在不同级别)和编译之后执行函数需要多少 CPU 周期。有没有办法分析代码或可执行文件以获得可重现的答案?我在 64 位 Windows 7 Pro 上使用 Eclipse
我想彻底测量和调整我的 C/C++ 代码,以便在 x86_64 系统上更好地使用缓存。我知道如何使用计数器(我的 Windows 机器上的 QueryPerformanceCounter)来测量时间,
我尝试将一些数据分组到每四周一次的存储桶中,并使用 pd.Grouper(key='created_at', freq='4W')。我希望这些组是这样的,如果我有从 2019-08-26 到 2019
我正在做一个关于随机数的大型学校项目,但我找不到 Math.random() 的句点。我安装了 7.0.800.15 版本,并且正在使用 Windows 10 计算机。我试过用一个简单的程序来确定周期
我正在努力解决我们生产环境中垃圾收集利用率高的问题,我想知道设置一个大的堆大小来保证老年代永远不会被填满是否会阻止触发主要的 GC 周期。 为了实现这一点,我想有一个特定的阈值标记会触发主要的 GC
我想测量在 Python 3 中执行加法运算所需的时钟周期数。 我写了一个程序来计算加法运算的平均值: from timeit import timeit def test(n): for i
我正在寻找一种方法来测量线程上的函数调用所花费的 cpu 周期。 示例伪代码: void HostFunction() { var startTick = CurrentThread.Cur
就 CPU 周期而言,malloc() 的成本是多少?(Vista/OS,最新版本的 gcc,最高优化级别,...) 基本上,我正在实现一个复杂的 DAG 结构(类似于链表)由一些 16B(不太常见)
C/C++ 中的类型转换会导致额外的 CPU 周期吗? 我的理解是,至少在某些情况下应该消耗额外的 CPU 周期。就像从浮点类型转换为整数一样,CPU 需要将浮点结构转换为整数。 float a=2.
我是一名优秀的程序员,十分优秀!