在前文中我们已经学习了:线程的基本情况、如何创建多线程、线程的生命周期。利用已有知识我们已经可以写出如何利用多线程处理大量任务这样简单的程序。但是当应用场景复杂时,我们还需要从管理控制入手,更好的操纵多线程。在第一节中我们讲过,使用多线程的好处之一就是我们可以通过编码和已有类库更好的管理和控制多线程。接下来我会详细的介绍如何管理多线程,包括:对线程的等待、守护线程、线程的睡眠、线程的突然停止、线程的让步、线程的优先级等。由于内容比较多,本节先介绍前两部分:对线程的等待、守护线程
1、线程的等待
我们常常对同一件事情进行切割,分成多干件小的事情后,再开辟多条线程来处理(有点分治的味道)。在多件事情处理完成之后我们需要再统一的处理。这样说有点枯燥:比如我们要导出一份文件,这个文件的行数非常多大概几十万行数据。直接导出也可以,会导致系统卡一下,严重一点的可能会超时。这时候怎么办呢?我们可以分成若干份,比如每五千条数据是一份数据,然后用一条线程去导出到文件中,这样就会生成若干份文件。当所有的文件都导出后,我们把这些数据汇总一下就OK了。但是这里有一个问题,什么时候所有线程都导完数据了呢?我们总不能一个一个的去检查文件是否存在吧。
这里java为我们提供了一个专门用于等待的方法Join().当某条线程执行了其他线程的join()方法以后,当前线程就会阻塞,直到其他线程执行结束以后,才可以运行。
如下述代码:
1 public class newThread extends Thread 2 { 3 public newThread(String name) 4 { 5 super(name); 6 } 7 public void run() 8 { 9 int i=100; 10 while(i-->0) 11 { 12 sleep(100); 13 } 14 } 15 16 public static void main(String args)throws Exception 17 { 18 Thread son_1=new newThread("thread-1"); 19 Thread son_2=new newThread("thread-2"); 20 son_1.start(); 21 son_2.start(); 22 son_1.join();//注意这里 23 son_2.join(); 24 System.out.println("all threads is over") 25 } 26 }
我们分别启动thread 1 和thread 2。这两条线程会分别运行,互相不影响,然后我们用主线程来等待线程1,直到它结束,我们才开始等待线程2。如果线程1比线程2的用时长的话,再我们等待完线程1 后,线程2将无需等待,直接输出结果。
join方法共有三种形式的重载
1 join()
2 join(long millis)//
3 join(long millis,int nanos)//millis 毫秒 nanos微秒
第一个方法时直到等待线程结束才继续向下运行,第二和第三个方法则是在限定时间内等待,如果时间结束将不会继续等待(可以用于超时判断),注意第三个方法并不常用,这里主要是因为java和硬件对时间的把控根本难以精确到微秒级,所以并不能很准确到控制。
2、守护线程
守护线程(Daemon Thread)又叫做精灵线程、后台线程。乍一听守护二字,觉得好像很厉害的意思,像是在保护其他线程的线程,其实很简单,并没有那么玄。所有的语言设计,都是为了更方便的使用。(防盗连接:本文首发自王若伊_恩赐解脱http://www.cnblogs.com/jilodream/ )
设想这样一个场景:在一个考场内,考生们都在埋头答卷,除了考生,还有监考老师,他的职责就是为了维护考场的秩序,对于有困难的考生提供帮助。当所有的考生都交卷后(或者时间到了以后,考生被强制交卷(结束线程)),这个监考老师的义务已经完成,无需继续运行。简而言之,这个监考老师是为了其他学生提供后台服务的,当所有的考生都已经停笔后,老师的义务也已经结束了,可以退场了。
有些人看到这里,可能会觉得,这还不简单,我们只要如线程等待让老师等待所有的学生都结束之后,不就可以了么?!这个回答对也不对。对的是老师的线程的确是在等待学生线程结束,不对的地方是老师线程在等待的同时,还在提供服务,并不是所有学生停笔后收卷这么简单。在Jvm中有一个所有开发者都熟悉的线程GC,它就是在其他线程运行的同时,默默的提供服务,当其他线程结束后,他又默默的退场。
如下代码
1 public class newThread extends Thread 2 { 3 public newThread(String name) 4 { 5 super(name); 6 } 7 public void run() 8 { 9 int i=100; 10 while(i-->0) 11 { 12 sleep(100); 13 } 14 } 15 16 public static void main(String args)throws Exception 17 { 18 Thread son_1=new newThread("thread-1"); 19 Thread son_2=new newThread("thread-2"); 20 Thread daemonThread=new newThread("thread-daemonThread"); 21 son_1.start(); 22 son_2.start(); 23 24 daemonThread.setDaemon(true);//注意看这里 25 daemonThread.start(); 26 son_1.join(); 27 son_2.join(); 28 System.out.println("all son_threads is over") 29 } 30 } 31 32 class DaemonThread 33 { 34 public DaemonThread(String name) 35 { 36 super(name); 37 } 38 public void run() 39 { 40 int i=1000; 41 while(i-->0) 42 { 43 sleep(100); 44 } 45 } 46 }
在代码中 Daemonthread线程被设置为后台线程,则当主线程等待子线程结束后, Daemonthread线程也会结束,不会继续运行。我们可以用这种方式设置一种线程,专门用来维护其他线程的正常运行,比如后台计数,计时,查询各个客户端是否保持在线(心跳),推送事件等。
java还专门提供了 IsDaemon()的方法来判断这个线程是否为后台线程。
这里有一点注意的是,如果要设置一个线程为后台线程,必须要在这个线程还没有开始“就绪”(start())时设置,否则会引发非法的线程状态异常