Java编程那些事儿95——多线程实现方法2
副标题#e#
12.2.2实现Runnable接口
一个类假如需要具备多线程的本领,也可以通过实现java.lang.Runnable接口举办实现。凭据Java语言的语法,一个类可以实现任意多个接口,所以该种实现方法在实际实现时的通用性要比前面先容的方法好一些。
利用实现Runnable接话柄现多线程的示例代码如下:
/**
* 测试类
*/
public class Test2 {
public static void main(String[] args) {
//建设工具
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
//启动
t.start();
try{
for(int i = 0;i < 10;i++){
Thread.sleep(1000);
System.out.println("main:" + i);
}
}catch(Exception e){}
}
}
/**
* 利用实现Runnable接口的方法实现多线程
*/
public class MyRunnable implements Runnable {
public void run() {
try{
for(int i = 0;i < 10;i++){
Thread.sleep(1000);
System.out.println("run:" + i);
}
}catch(Exception e){}
}
}
该示例代码实现的成果和前面实现的成果沟通。在利用该方法实现时,使需要实现多线程的类实现Runnable,实现该接口需要包围run要领,然后将需要以多线程方法执行的代码书写在run要领内部或在run要领内部举办挪用。
在需要启动线程的处所,首先建设MyRunnable范例的工具,然后再以该工具为基本建设Thread类的工具,最后挪用Thread工具的start要领即可启动线程。代码如下:
//建设工具
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
//启动
t.start();
在这种实现方法中,大部门和前面先容的方法雷同,启动的代码稍微贫苦一些。这种方法也是实现线程的一种主要方法。
#p#副标题#e#
12.2.3利用Timer和TimerTask组合
最后一种实现多线程的方法,就是利用java.util包中的Timer和TimerTask类实现多线程,利用这种方法也可以较量利便的实现线程。
在这种实现方法中,Timer类实现的是雷同闹钟的成果,也就是按时可能每隔一按时间触发一次线程。其实,Timer类自己实现的就是一个线程,只是这个线程是用来实现挪用其它线程的。而TimerTask类是一个抽象类,该类实现了Runnable接口,所以凭据前面的先容,该类具备多线程的本领。
在这种实现方法中,通过担任TimerTask使该类得到多线程的本领,将需要多线程执行的代码书写在run要领内部,然后通过Timer类启动线程的执行。
在实际利用时,一个Timer可以启动任意多个TimerTask实现的线程,可是多个线程之间会存在阻塞。所以假如多个线程之间假如需要完全独立运行的话,最好照旧一个Timer启动一个TimerTask实现。
利用该种实现方法实现的多线程示例代码如下:
import java.util.*;
/**
* 测试类
*/
public class Test3 {
public static void main(String[] args) {
//建设Timer
Timer t = new Timer();
//建设TimerTask
MyTimerTask mtt1 = new MyTimerTask("线程1:");
//启动线程
t.schedule(mtt1, 0);
}
}
import java.util.TimerTask;
/**
* 以担任TimerTask类的方法实现多线程
*/
public class MyTimerTask extends TimerTask {
String s;
public MyTimerTask(String s){
this.s = s;
}
public void run() {
try{
for(int i = 0;i < 10;i++){
Thread.sleep(1000);
System.out.println(s + i);
}
}catch(Exception e){}
}
}
在该示例中,MyTimerTask类实现了多线程,以多线程方法执行的代码书写在该类的run要领内部,该类的成果和前面的多线程的代码实现雷同。
而在该代码中,启动线程时需要首先建设一个Timer类的工具,以及一个MyTimerTask线程类的兑现,然后利用Timer工具的schedule要领实现,启动线程的代码为:
//建设Timer
Timer t = new Timer();
//建设TimerTask
MyTimerTask mtt1 = new MyTimerTask("线程1:");
//启动线程
t.schedule(mtt1, 0);
个中schedule要领中的第一个参数mtt1代表需要启动的线程工具,而第二个参数0则代表延迟0毫秒启动该线程,也就是立即启动。
由于schedule要领较量重要,下面具体先容一下Timer类中的四个schedule要领:
1、public void schedule(TimerTask task,Date time)
#p#分页标题#e#
该要领的浸染是在达到time指定的时间或已经高出该时间时执行线程task.譬喻假设t是Timer工具,task是需要启动的TimerTask线程工具,后续示例也回收这种约定实现,则启动线程的示例代码如下:
Date d = new Date(2009-1900,10-1,1,10,0,0);
t. schedule(task,d);
则该示例代码的浸染是在时间到达d指定的时间或高出该时间(譬喻2009年10月2号)时,启动线程task.
2、public void schedule(TimerTask task, Date firstTime, long period)
该要领的浸染是在时间达到firstTime开始,每隔period毫秒就启动一次task指定的线程。示例代码如下:
Date d = new Date(2009-1900,10-1,1,10,0,0);
t. schedule(task,d,20000);
该示例代码的浸染是其时间到达或高出d指定的时间今后,每隔20000毫秒就启动一次线程task,这种方法会反复触发线程。
3、public void schedule(TimerTask task,long delay)
该要领和第一个要领雷同,浸染是在执行schedule要领今后delay毫秒今后启动线程task.示例代码如下:
t. schedule(task,1000);
该示例代码的浸染是在执行该行启动代码1000毫秒今后启动一次线程task.
4、public void schedule(TimerTask task,long delay,long period)
该要领和第二个要领雷同,浸染是在执行schedule要领今后delay毫秒今后启动线程task,然后每隔period毫秒反复启动线程task.
破例需要说明的是Timer类中启动线程还包括两个scheduleAtFixedRate要领,这两个要领的参数和上面的第二个和第四个一致,其浸染是实现反复启动线程时的准确延时。对付schedule要领来说,假如反复的时距离断是1000毫秒,则实际的延迟时间是1000毫秒加上系统执行时耗损的时间,譬喻为5毫秒,则实际每轮的时距离断为1005毫秒。而对付scheduleAtFixedRate要领来说,假如配置的反复时距离断为1000毫秒,系统执行时耗损的时间为5毫秒,则延迟时间就会酿成995毫秒,从而担保每轮隔断为1000毫秒。
先容完了schedule要领今后,让我们再来看一下前面的示例代码,假如在测试类中启动两个MyTimerTask线程,一种实现的代码为:
import java.util.Timer;
/**
* 测试类
*/
public class Test4 {
public static void main(String[] args) {
//建设Timer
Timer t = new Timer();
//建设TimerTask
MyTimerTask mtt1 = new MyTimerTask("线程1:");
MyTimerTask mtt2 = new MyTimerTask("线程2:");
//启动线程
System.out.println("开始启动");
t.schedule(mtt1, 1000);
System.out.println("启动线程1");
t.schedule(mtt2, 1000);
System.out.println("启动线程2");
}
}
在该示例代码中,利用一个Timer工具t依次启动了两个MyTimerTask范例的工具mtt1和mtt2.而措施的执行功效是:
开始启动
启动线程1
启动线程2
线程1:0
线程1:1
线程1:2
线程1:3
线程1:4
线程1:5
线程1:6
线程1:7
线程1:8
线程1:9
线程2:0
线程2:1
线程2:2
线程2:3
线程2:4
线程2:5
线程2:6
线程2:7
线程2:8
线程2:9
从措施的执行功效可以看出,在Test4类中mtt1和mtt2都被启动,凭据前面的schedule要领先容,这两个线程均会在线程启动今后1000毫秒后得到执行。可是从实际执行结果却可以看出这两个线程不是同时执行的,而是依次执行,这主要是因为一个Timer启动的多个TimerTask之间会存在影响,当上一个线程未执行完成时,会阻塞后续线程的执行,所以当线程1执行完成今后线程2才得到了执行。
假如需要线程1和线程2得到同时执行,则只需要别离利用两个Timer启动TimerTask线程即可,启动的示例代码如下:
import java.util.Timer;
/**
* 测试类
*/
public class Test5 {
public static void main(String[] args) {
//建设Timer
Timer t1 = new Timer();
Timer t2 = new Timer();
//建设TimerTask
MyTimerTask mtt1 = new MyTimerTask("线程1:");
MyTimerTask mtt2 = new MyTimerTask("线程2:");
//启动线程
System.out.println("开始启动");
t1.schedule(mtt1, 1000);
System.out.println("启动线程1");
t2.schedule(mtt2, 1000);
System.out.println("启动线程2");
}
}
在该示例中,别离利用两个Timer工具t1和t2,启动两个TimerTask线程工具mtt1和mtt2,两者之间不相互滋扰,所以到达了同时执行的目标。
#p#分页标题#e#
在利用上面的示例举办运行时,由于Timer自身的线程没有竣事,所以在措施输出完成今后措施还没有竣事,需要手动竣事措施的执行。譬喻在Eclipse中可以点击节制台上面的赤色“Teminate”按钮竣事措施。
12.2.4 小结
关于线程的三种实现方法,就简朴的先容这么多。其实无论那种实现方法,都可以实现多线程,在语法答允的前提下,可以利用任何一种方法实现。较量而言,实现Runnable接口方法要通用一些。
只是从语法角度先容线程的实现方法,照旧无法体会到线程实现的玄妙,下面将通过几个简朴的示例来体会线程成果的强大,并体会并发编程的神奇,从而可以或许进入并发编程的规模发挥技能的优势。