线程创建后,可以执行start()方法启动线程,根据线程任务的特性和线程之间的协调性要求,需要对线程进行控制。对线程的控制通常是通过调用Thread对象的方法实现的,主要有sleep()、suspend()、resume()、join()、interrupt()和stop方法。一般情况下方法的调用会引起线程状态的转变。
1、使用Sleep暂停执行
Thread.sleep()使当前线程的执行暂停一段指定的时间,这可以有效的使应用程序的其他线程或者运行在计算机上的其他进程可以使用处理器时间。该方法不会放弃CPU之外的其他资源。Sleep有两个重载的版本,一个以毫秒指定睡眠时间,另一个以纳秒指定睡眠时间,但并不保证这些睡眠时间的精确性,因为它们收到系统计时器和调度程序精度和准确性的影响。另外中断(interrupt)可以终止睡眠时间,在任何情况下,都不能假设调用sleep就会按照知道指定
的时间准确的挂起线程。
public class TestSleep {
public static void main(String[] arg) {
String[] args={"one","two","three","four"};
long start=System.nanoTime();
for(int i=0;i<args.length;i++){
System.out.println(args[i]);
//休眠主线程
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end=System.nanoTime();
System.out.println("总的时间:"+(end-start)/1000000);
}
}
需要注意的是,sleep()方法声明可能会抛出InterruptedException异常,当另一个线程中断了已经启动sleep的当前线程时机会抛出这个异常。上面的程序只有主线程,不需要考虑这个问题。
2、使用join等待另外一个线程结束
join方法让一个线程等待另一个线程的完成,如果t1、t2是两个Thread对象,在t1中调用t2.join(),会导致t1线程暂停执行,直到t2的线程终止。join的重载版本运行程序员指定等待的时间,当时和sleep一样,这个时间是不精确地。
public class TestJoin extends Thread{
static int result=0;
public TestJoin(String name){
super(name);
}
public static void main(String[] args) {
System.out.println("主线程执行");
Thread t=new TestJoin("计算线程");
t.start();
System.out.println("result:"+result);
try {
long start=System.nanoTime();
t.join();
long end=System.nanoTime();
System.out.println((end-start)/1000000+"毫秒后:"+result);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
System.out.println(this.getName()+"开始计算:...");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
result=(int) (Math.random()*10000);
System.out.println(this.getName()+"计算结束:...");
}
}
执行结果如下:
主线程执行
result:0
计算线程开始计算:...
计算线程计算结束:...
3993毫秒后:4462
上面的程序中,计算线程在计算的时候休眠了4000毫秒,在主线程中调用了t.join()后,主线程等待计算线程执行结束,然后输出结果。
可以把t.join()修改为t.join(2000)。
public class TestJoin extends Thread{
static int result=0;
public TestJoin(String name){
super(name);
}
public static void main(String[] args) {
System.out.println("主线程执行");
Thread t=new TestJoin("计算线程");
t.start();
System.out.println("result:"+result);
try {
long start=System.nanoTime();
t.join(2000);
long end=System.nanoTime();
System.out.println((end-start)/1000000+"毫秒后:"+result);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
System.out.println(this.getName()+"开始计算:...");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
result=(int) (Math.random()*10000);
System.out.println(this.getName()+"计算结束:...");
}
}
观察输出结果,发现主线程并没有等待计算线程执行结束,就输出结果了。
主线程执行
result:0
计算线程开始计算:...
1990毫秒后:0
计算线程计算结束:...
3、使用中断(Interrupt)取消线程
已经启动的线程是活跃的,即isAlive()方法返回trur,线程终止之前一直是活跃的。有三种方法可以使线程终止:
1)run()方法正常返回;
2)run()方法以外结束;
3)应用程序终止。
经常会碰到这样的情况,我们创建了执行某项工作的线程,然后再它完成之前需要取消这样工作。要使线程在完成任务之前可取消,必须采取一定的措施,但应该是一个清晰而安全的机制使线程终止。我们可以通过中断(Thread.interrupt)线程来请求取消,并且让线程来监视并响应中断。中断请求通常是用户希望能够终止线程的执行,但并不会强制终止线程,但是它会中断线程的睡眠状态,比如调用sleep和wait方法后。线程自己检查中断状态并终止线程比直接调用stop方法要安全很多,因为线程可以保存自己的状态。并且stop()方法已经不推荐使用了。
和中断线程有关的方法有:
1)interrupt,向线程发送中断;
2)isInterrupted,测试线程是否已经被中断;
3)Interrupt,测试线程是否已经被中断,随后清除线程的“中断”状态。
线程的中断状态只有线程自己清除,当线程侦测到自己被中断时,经常需要在响应中断之前做某些清除工作,这些清除工作可能涉及那些在线程仍然保持中断状态时会受到影响的操作。如果被中断的线程正在执行sleep,或者wait方法,就会抛出InterruptException异常。这种抛出异常的中断会清除线程的中断状态。大体上任何执行阻塞操作的方法,都应该通过Interrupt来取消阻塞操作。
下面的程序,主线程在等待计算线程2000毫秒后,中断计算线程,计算线程由于正在执行sleep,就会抛出InterruptException异常,终止休眠状态,然后进入异常处理,在catch中可以做一些清理工作(如果需要),然后线程执行结束。
这是一种典型的终止线程执行的方法:
public class TestInterrupt extends Thread {
static int result=0;
public TestInterrupt(String name){
super(name);
}
public static void main(String[] args) {
System.out.println("主线程执行");
Thread t=new TestInterrupt("计算线程");
t.start();
System.out.println("result:"+result);
try {
long start=System.nanoTime();
t.join(2000);
long end=System.nanoTime();
t.interrupt();
System.out.println((end-start)/1000000+"毫秒后:"+result);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
System.out.println(this.getName()+"开始计算:...");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
System.out.println(this.getName()+"被中断,结束");
return;
}
result=(int) (Math.random()*10000);
System.out.println(this.getName()+"计算结束:...");
}
}
输出结果如下:
主线程执行
result:0
计算线程开始计算:...
1991毫秒后:0
计算线程被中断,结束
从输出结果中可以看出,计算线程被中断后,run()方法中的最后两行语句没有执行。没有产生计算结果。
t.interrupt()不会中断正在执行的线程,只是将线程的标志位设置成true。但是如果线程在调用sleep(),join(),wait()方法时线程被中断,则这些方法会抛出InterruptedException,在catch块中捕获到这个异常时,线程的中断标志位已经被设置成false了,因此在此catch块中调用t.isInterrupted(),Thread.interrupted()始终都为false。
如果一个线程长时间没有调用能够抛出InterruptException异常的方法,那么线程就必须定期的调用Thread.interrupted方法,如果接收到中断就返回true,然后就可以退出线程。
public class TestInterrupt extends Thread {
static int result=0;
public TestInterrupt(String name){
super(name);
}
public static void main(String[] args) {
System.out.println("主线程执行");
Thread t=new TestInterrupt("计算线程");
t.start();
System.out.println("result:"+result);
try {
long start=System.nanoTime();
t.join(10);
long end=System.nanoTime();
t.interrupt();
System.out.println((end-start)/1000000+"毫秒后:"+result);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
System.out.println(this.getName()+"开始计算...");
for(int i=0;i<100000;i++){
result++;
if(Thread.interrupted()){
System.out.println(this.getName()+"被中断");
return;
}
}
System.out.println(this.getName()+"计算结束...");
}
}
输出结果如下:
result:0
计算线程开始计算...
计算线程被中断
7毫秒后:18563
false
上面的程序,计算线程原计划执行100000次循环,主线程等待10毫秒后,中断计算线程,计算线程接收到中断后,就可以结束执行了。在更加复杂的应用程序中,当线程收到中断信号后,抛出InterruptException异常更有意义。把中断处理代码集中到catch字句中。
4、使用stop终止线程
在Thread类中提供了Stop方法,来强迫线程停止执行。但是现在已经过时了。
该方法具有固定的不安全性。用Thread.Stop来终止线程将释放它已经锁定的所有监视器(作为沿堆栈向上传播的未检查 ThreadDeath异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其他线程可见,这有可能导致任意的行为。Stop的许多使用方式都应由只修改某些变量以知识目标线程应该停止运行的代码来取代。目标线程应定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。如果目标线程等待很长时间(例如基于一个条件变量),则应使用interrupt方法来中断该等待。
无论该线程在做些什么,它所代表的线程都被迫异常停止,并抛出一个新创建的ThreadDeath对象作为异常。停止一个尚未启动的线程是允许的。如果最后启动了该线程,它会立即终止。
应用程序通常不应视图捕获ThreadDeath,除非它必须执行某些异常的清楚操作(注意,抛出ThreadDeath将导致try语句的finally字句在线程终止前执行)。如果catch字句捕获了一个ThreadDeath对象,则重新抛出该对象很重要,因为这样该线程才会真正终止。
对其它为捕获的异常做出反应的顶级错误处理不会打印输出消息,或者另外通知应用程序为捕获到的已换成那个是否为ThreadDeath的一个实例。
5、结束程序的执行
每个应用程序都从执行main的线程开始的,如果应用程序没有创建任何其它的线程,那么main方法返回时,应用程序就结束了,但是如果应用程序创建了其它线程,就要根据线程的类型分情况来考虑了。
线程一般分为两种:用户线程和守护线程。用户线程的存在可以使应用程序保持运行状态,而守护线程则不会。当最后一个用户线程结束时,所有守护线程都会被终止,应用程序也随之结束。守护线程的终止,很像调用destroy所产生的终止,事发突然,没有几乎做任何清除,所以应该考虑清楚,用守护线程执行哪种类型的任务。使用Thread.setDaemon(true)可以把线程标记为守护线程。默认情况下,线程的守护状态继承自创建它的线程。
一般main线程是程序运行时第一个启动的线程,称作为初始线程。如果希望应用程序在初始线程消亡后就退出,就可以把所创建处理的线程都标记为守护线程。我们也可以通过调用System,或者Runtime的exit方法来强制应用程序结束,这个方法将终止Java虚拟机的当前执行过程。
分享到:
相关推荐
《JAVA并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。它涵盖了Java并发的核心概念、工具和最佳实践,旨在帮助读者在多线程环境下编写高效、安全的代码。 并发编程是现代软件开发中的关键技能,...
《Java并发编程实践》是一本深入探讨Java多线程编程的经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和David Holmes等专家共同编写。这本书全面介绍了Java平台上的并发编程技术,是Java开发...
《JAVA并发编程实践》既能够成为读者的理论支持,又可以作为...《JAVA并发编程实践》适合于具有一定Java编程经验的程序员、希望了解Java SE 5以及6在线程技术上的改进和新特性的程序员,以及Java和并发编程的爱好者。
Java并发编程实践是Java开发中不可或缺的一个领域,它涉及到如何高效、正确地处理多线程环境中的任务。这本书的读书笔记涵盖了多个关键知识点,旨在帮助读者深入理解Java并发编程的核心概念。 1. **线程和进程的...
《JAVA并发编程实践》随着多核处理器的普及,使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序中取得了显著的进步,提高了Java虚拟机的性能以及并发类的可伸缩性,并加入了丰富的新并发构建块。在...
《Java并发编程实践》是一本深入探讨Java多线程编程的权威著作,它详细阐述了在并发环境下如何设计和实现高效、可靠的程序。这本书涵盖了Java并发编程的核心概念、工具和最佳实践,对于想要提升Java并发编程技能的...
《JAVA并发编程实践》适合于具有一定Java编程经验的程序员、希望了解Java SE 5以及6在线程技术上的改进和新特性的程序员,以及Java和并发编程的爱好者。 作者简介 作者:(美)戈茨 等 本书作者系lava标准化组织...
《JAVA并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。并发编程是现代软件开发中的核心主题,尤其是在多核处理器普及的今天,利用好并发能够显著提升程序的执行效率和系统性能。这本书以实践为...
Java线程与并发编程实践是Java开发者必备的技能之一,特别是在多核处理器和高并发应用环境中,有效地管理和利用线程能极大地提升程序的性能。本书《java线程与并发实践编程》由Jeff Friesen撰写,2017年2月出版,...
### Java并发编程实践知识点详解 #### 一、Java并发编程基础 ##### 1.1 并发与并行概念区分 在Java并发编程实践中,首先需要理解“并发”与“并行”的区别。“并发”指的是多个任务同时进行,但实际上可能是在多...
书中会首先介绍Java并发编程的基础知识,包括线程的创建和运行,同步机制的基本用法,以及Java内存模型的相关概念。随着章节的深入,作者可能会更深入地讲解Java提供的并发工具,例如锁、原子变量、线程池、以及并发...
### Java并发编程实践 #### 书籍概述 《Java并发编程实践》是一本由Brian Goetz等人编写的关于Java并发编程的经典著作。本书深入浅出地介绍了Java 5.0及之后版本中新增加的并发特性,并对并发编程进行了全面而详尽...
《Java并发编程实践》这本书深入探讨了Java平台上的并发编程技术,涵盖了从基础概念到高级策略的广泛主题。在Java编程中,并发处理是优化性能、提高系统资源利用率的关键手段,尤其是在多核处理器和分布式系统中更为...
《Java并发编程实践》是关于Java语言在并发编程领域的实践指南,作者在本书中详细介绍了在Java编程中,如何高效地实现多线程程序的设计和开发。本书不仅为初学者提供了理论基础,还为有经验的开发者提供了优化并发...
### Java并发编程实践 #### 一、并发编程基础 ##### 1.1 并发与并行的区别 在Java并发编程中,首先需要理解“并发”(Concurrency)和“并行”(Parallelism)的区别。“并发”指的是多个任务在同一时间段内交替...