要产生一个线程,有两种方法:
◆需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法;◆实现Runnable接口,重载Runnable接口中的run()方法。
具体步骤
1、扩展Thread类来创建线程
首先,需要通过创建一个新类来扩展Thread类,这个新类就成为Thread类的子类。接着在该子类中重写Thread类的run()方法,此时方法体内的程序就是将来要在新建线程中执行的代码。
示例如下所示:
class SubThread extends Thread
{
…
public void run()
{
//新建线程所要完成的工作}
…
}
接着要创建该子类的对象,此时一个新的线程就被创建了,创建新线程时会用到Thread类定义的如下两个构造函数:
◆public Thread()
◆public Thread(String name)
其中,第一个构造函数是Thread类默认的构造函数,不指定参数;第二个构造函数可以为新建的线程指定一个名称,该名称就是字符串参数name的值。
建立了新的线程对象以后,它并不运行,而是直到调用了该对象的start()方法,该方法在Thread类中定义,在Thread类的子类中被覆盖。它的作用是启动一个新的线程,并在该线程上运行子类对象中的run()方法。
start()方法声明格式为:
public void start()
示例如下所示:
SubThread s = new SubThread(); //创建Thread子类的对象s.start(); //启动一个新的线程
当一个类继承Thread类时,它必须重写run()方法,这个方法是新线程的入口。如果Thread类的这个子类没有覆盖run()方法,那么程序会调用Thread类的run()方法,只不过该run()方法什么也不做,此时新线程一创建就结束了。这种线程对程序来说是没有任何意义的,所以在这里提醒读者在创建线程的时候一定不要忘了覆盖Thread类的run()方法。
下面举一个完整的例子来演示通过扩展Thread类创建线程的过程。程序代码如下所示:
//例4.1.1ThreadDemo.java
class NewThread extends Thread //通过继承Thread类来创建一个新的线程{
NewThread()//构造方法{
super("Thread Demo"); //定义线程的名字System.out.println("New thread: " + getName());
}
public void run() //覆写run()方法,这是线程的入口{
while( true )
{
System.out.println(Thread.currentThread().getName()+
" is running");
}
}
}
class ThreadDemo
{
public static void main(String args[])
{
NewThread thd = new NewThread(); //创建一个新线程thd.start(); //启动线程,调用NewThread类对象的run()方法}
}
可以看到,新的线程是由实例化NewThread类的对象创建的,该NewThread类可以通过继承java.lang.Thread类来得到。其中,在NewThread类中,构造函数里调用了super()方法。该方法将调用父类Thread下列形式的构造函数:
public Thread(String threadName)
这里,threadName指定线程名称(当然也可以不指定线程的名称,而由系统自动为新建线程提供名称)。在NewThread类中通过覆写run()方法来规定线程所要实现的内容。此外,需要注意的是,启动新线程执行时必须调用start()方法。程序结果如图4.1.1所示:
在上面的代码中,使用了Thread类的currentThread()静态方法来获得当前程序执行时所对应的那个线程对象,又通过线程对象的getName()方法,得到了当前线程的名字。这些方法都可以在JDK帮助文档中查到。因此,善于利用JDK帮助文档来获取有关类的更多信息,可以方便 程序的编写。
在上面代码的run()方法中,由于循环条件始终为true,因此,屏幕上会不断地输出Thread Demo is running,新建的线程永远不会结束,这当然不是所希望的结果。这里所希望的是可以合理的设置循环条件来有效地控制线程的终止。所以,在run()方 法中使用到循环控制的时候一定要小心使用,否则局面难以控制。
其实,针对前面的程序做一些改动。可以让这个程序实现一个非常有用的功能。
//例4.1.2ThreadDemo2.java
class NewThread extends Thread //通过继承Thread类来创建一个新的线程{
NewThread(String name) //构造方法{
super(name);
}
public void run() //重写run()方法,这是线程的入口{
for( int i=10 ; i>0 ;i--) //循环执行10次{
try
{
System.out.println("left time: "+ i);
Thread.sleep(1000); //当前线程睡眠1000毫秒}catch(InterruptedException e){ //处理异常System.out.println(e.getMessage());
}
}
System.out.println("game is over,bye!");
}
}
class ThreadDemo2
{
public static void main(String args[])
{
NewThread thd = new NewThread("Thread Demo"); //创建一个新的线程thd.start(); //启动线程,调用NewThread类对象的run()方法}
}
程序输出结果如图4.1.2所示:
通过这个程序看到了什么?在run()方法体中,实现了一个倒计时的功 能。线程通过循环控制,每隔一秒输出一次剩余的时间,循环结束时输出"game is over,bye!",线程也随之结束。可见这个程序中新建的线程不是死循环,而是通过一些条件来对线程的起始进行了控制,从而实现了倒计时的功能。
在这个程序中,还可以看到,在给线程起名字的时候可以通过创建线程的时候来实现。因为查阅JDK的帮助文档,会发现Thread类除了默认的构造函 数之外,还有很多带参数的构造函数,只不过在这里是用到了public Thread(String name)这个构造方法。
并不是在创建线程对象的时候给线程起个名字就可以了,还应该在线程类的子类中定义相应的构造函数才行,这个构造函数的形式如下:
SubThread(String name) //线程子类的构造方法{
super(name);
}
如果不这样做,程序编译会提示错误,读者可以想想为什么。也可以将上面程序中NewThread类的构造方法注释掉,编译一下程序,看到错误提示后,再去思考这个问题。
在这个程序中还用到了try…catch语句,它用来捕获程序中可能发生的异常,而产生异常的原因是程序中使用了Thread.sleep()这样的方法。通过查阅JDK的帮助文档,可以看到线程类的sleep()方法的完整格式如下:
public static void sleep(long millis) throws InterruptedException
看到这个throws关键字,想必读者就应知道为什么使用try…catch语句了。由于这个方法可能会抛出一个中断异常,因此,有必要在程序调用这个方法时对可能发生的异常进行处理。此外,这个方法是静态的,所以可以通过类名直接调用。
2、实现Runnable接口来创建线程
除了扩展Thread类可以创建线程之外,还可以通过定义一个实现了Runnable接口的类来创建线程。为了将来程序执行时可以进入线程,在这个类中必须实现Runnable接口中唯一提供的run()方法。
示例如下所示:
class OneThread implements Runnable
{
…
public void run()
{
//新建线程所要完成的工作}
}
当定义好一个实现了Runnable接口的类以后,还不能直接去创建线程对象,要想真正去创建一个线程,还必须在类的内部实例化一个Thread类的对象。此时,会用到Thread类定义的如下两个构造函数:
public Thread(Runnable target)
public Thread(Runnable target,String name)
在这两个构造函数中,参数target定义了一个实现了Runnable接口的类的对象引用。新建的线程将来就是要执行这个对象中的run()方法。而新建线程的名字可以通过第二个构造方法中的参数name来指定。
示例如下所示:
OneThread onethread = new OneThread();
Thread newthread = new Thread(onethread);
此时,新线程对象才被创建,如果想要执行该线程的run()方法,则仍然需要通过调用start()方法来实现。例如:
newthread.start();
要想创建新的线程对象,这两条语句缺一不可。此后程序会在堆内存中实实在在地创建一个OneThread类的实例对象,该对象中包含了一个线程对象newthread。newthread对象会通过调用start()方法来执行它自己的run()方法。随着run()方法的结束,线程对象newthread的生命也将结束,但是onethread对象还会存在于堆内存当中。如果希望在实际编程当中一旦线程结束,即释放与线程有关的所有资源,可以使用创建匿名对象的方法来创建这个线程,格式如下所示:
new Thread(new OneThread).start();
这样一来,该线程一旦运行结束,所有与该线程有关的资源都将成为垃圾,这样就可以在特定的时间内被Java的垃圾回收机制予以回收,释放所占用内存,提高程序的效率。
下面这个程序是通过实现Runnable接口来创建的线程,可以将它和前面的例4.1.2的程序进行比较。
//例4.1.3ThreadDemo3.java
class NewThread implements Runnable //实现了Runnable接口{
public void run() //覆写Runnable接口中唯一的run()方法,这是线程的入口{
for( int i=10 ; i>0 ;i--)
{
try
{
System.out.println("left time: "+ i);
Thread.sleep(1000); //当前线程睡眠1000毫秒}catch(InterruptedException e){ //处理异常System.out.println(e.getMessage());
}
}
System.out.println("game is over,bye!");
}
}
class ThreadDemo3
{
public static void main(String args[])
{
NewThread newthread = new NewThread();
Thread thd = new Thread(newthread, "Thread Demo")
thd.start(); //启动线程,调用run()方法}
}
编译并运行这个程序,可以看到程序执行的结果和例4.1.2的程序输出的结果是完全一样的,因此,读者可以在创建线程的时候选择任意一种方式来实现
为了使程序达到优异的性能,可以利用创建线程来完成那些任务。因为一个线程就是一个独立的执行通道,在没有特殊的要求之下,多个线程之间彼此独立运行,互不干扰。而且带线程的程序通常比没有带线程的程序运行得要快,因此线程常 用在网络和图形用户界面等程序设计当中,这一优势在多处理器的计算机上更加明显。本节中不仅要理解什么是线程,而且还应掌握两种创建线程的方法,为以后在程序中使用多线程技术打下坚实的基础。
分享到:
2011-12-06 10:51
浏览 9807
评论