Windows操作系统是多任务操作系统,它以进程为单位。一个进程是一个包含有自身地址的程序,每个独立执行的程序都称为进程,也就是正在执行的程序。系统可以分配给每个进程一段有限的使用CPU的时间,CPU在这段时间中执行某个进程,然后下一个时间片又跳至另一个进程中去执行,由于CPU转换较快,所以使得每个进程好像是同时执行一样。
一个线程则是进程中的执行流程,一个进程中可以同时包括多个线程,每个线程也可以得到一小段程序的执行时间,这样一个进程就可以具有多个并发执行的线程,在单线程中程序代码按调用顺序依次往下执行,如果需要一个进程同时完成多段代码的操作,就需要产生多线程。
在Java中主要提供两种方式实现线程,分别为继承java.lang.Thread类与实现java.lang.Runnable接口。
1 . 继承Thread类。
Thread类是java.lang包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程城需要建立Thread实例。Thread类中常用的两个构造方法如下:
public Thread():创建一个新的线程对象。
public Thread(String threadName):创建一个名称为threadName的线程对象。
继承Thread类创建一个新的线程的语法如下:
public class ThreadTest extends Thread{
}
完成线程真正功能的代码放在类的run()方法中,当一个类继承Thread类后,就可以在该类中覆盖run()方法,将实现该线程功能的代码写入run()方法中,然后同时调用Thread类中的start()方法执行线程,也就是调用run()方法。
Thread对象需要一个任务来执行,任务是指线程在启东市执行的工作,该工作的功能代码被写在run()方法中,run()方法必须使用以下语法格式:
public void run(){
}
PS:如果start()方法调用一个已经启动的线程,系统将抛出IllegalThreadStateException异常。
当执行一个线程程序时就自动产生一个线程,主方法正是在这个线程上运行的,当不再启动其他线程时,该程序就为单线程程序。主方法线程启动由JAVA虚拟机负责,程序员负责启动自己的线程。
public static void (String[] args){
new ThreadTest().start();
}
下面看一个继承Thread类的实例。
/*
指定类继承Thread类。
*/
public class ThreadTest extends Thread{
private int count = 10;
// 1 . 重写run()方法。
public void run(){
while (true){
// 2 . 打印count变量。
System.out.print(count + "");
// 3 . 使count变量自减,当自减为0时,退出循环。
if(--count == 0){
return;
}
}
}
public static void main(String[] args) {
new ThreadTest().start();
}
}
在上述实例中继承了Thread类,然后在类中覆盖了run()方法,通常在run()方法中使用无限循环的形式,使得线程一直运行下去,所以要指定一个跳出循环的条件。如本实例中使用变量count递减为0作为跳出循环的条件,在main()方法中实现成执行需要调用Thread类中的start()方法,start()方法调用被覆盖的run()方法,如果不调用start()方法,线程永远都不会启动,在主方法没有调用start()方法之前。Thread对象只是一个实例,而不是一个真正的线程。
2 . 实现Runnable接口。
到目前为止,线程都是通过扩展Thread类来创建的,如果程序员需要继承其他类非Thread类,而且还要使当前类实现多线程,那么可以通过接口来实现,例如一个扩展JFrame类的GUI程序不可能再继承Thread类,因为JAVA语言中不支持多继承,这时该类就需要实现Runnable接口使其具有使用线程的功能。
实现Runnable接口的语法如下:
public class Thread extends Object implement Runnable
PS:实质上,Thread类实现了Runnable接口,其中的run()方法正式对Runnable接口中的run()方法的具体实现。
实现Runnable接口的程序会创建一个Thread对象,并将Runnable对象与Thread对象相关联。Thread类中有一下两个构造方法:
public Thread(Runnable target)
public Thread(Runnable target,String name).
这两个构造方法的参数中都存在Runnable实例,使用以上构造方法就可以将Runnable实例与Thread实例相关联。
使用Runnable接口启动新的线程的步骤如下:
1 . 建立Runnable对象。
2 . 使用参数为Runnable对象的构造方法创建Thread实例。
3 . 调用start()方法启动线程。
线程最引人注目的部分应该是与Swing相结合创建GUI程序,下面演示一个GUI程序,该程序结合了图标滚动的功能。
import javax.swing.*;
import java.awt.*;
import java.net.URL;
public class SwingAndThread extends JFrame {
/*
在项目中创建SwingAndThread类,该类继承了JFrame类,实现图标移动的功能,其中使用了Swing与线程相结合的技术。
*/
// 1 . 声明JLabel对象。
private JLabel jl = new JLabel();
// 2 . 声明线程对象。
private static Thread t;
// 3 . 声明计数变量。
private int count = 0;
// 4 . 声明容器。
private Container container = getContentPane();
public SwingAndThread(){
// 5 . 绝对定位窗体大小与位置。
setBounds(300,200,250,100);
// 6 . 使窗体不使用任何布局管理器。
container.setLayout(null);
// 7 . 获取图片的URL。
URL url = SwingAndThread.class.getResource("/1.gif");
// 8 . 实例化一个Icon。
Icon icon = new ImageIcon(url);
// 9 . 将图标放置在标签中。
jl.setIcon(icon);
// 10 . 设置图片在标签的最左上方。
jl.setHorizontalAlignment(SwingConstants.LEFT);
// 11 . 设置标签的位置与大小。
jl.setBounds(10,10,200,50);
jl.setOpaque(true);
// 12 . 定义匿名内部类,该类实现Runnable接口。
t = new Thread(new Runnable() {
// 13 . 重写run()方法。
@Override
public void run() {
// 14 . 设置循环条件.
while(count <= 200){
// 15 . 将标签的横坐标用变量表示。
jl.setBounds(count,10,200,50);
try{
// 16 . 使线程休眠1000ms。
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
// 17 . 使横坐标每次增加4.
count += 4;
// 18 . 当图标到达标签的最右边时,使其回到标签最左边。
if(count == 200){
count = 10;
}
}
}
});
// 19 . 启动线程。
t.start();
// 20 . 将标签添加到容器中。
container.add(jl);
// 21 . 使窗体可见。
setVisible(true);
// 22 . 设置窗体的关闭方式。
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
public static void main(String[] args) {
// 23 . 实例化一个SwingAndThread对象。
new SwingAndThread();
}
}
关于图片的URL问题,在文件夹里找生成的.class文件,找到之后将图片与.class文件放在一个文件夹里即可。
在本实例中,为了使图标具有滚动功能,需要在类的构造方法中创造Thread的实例,在创建该实例的同时,需要Runnable对象作为Thread类构造方法的参数,然后使用内部类形式实现run()方法,然后在run()方法中主要循环图标的横坐标位置,当图标横坐标到达标签最右方时,再次将图标的横坐标至于图标滚动的初始位置。
PS:启动一个新的线程不是直接调用Thread子类对象的run()方法,而是调用Thread子类的start()方法,Thread类的start()方法产生一个新的线程,该线程运行子类的run()方法。