java提供了两种方式实现线程,分别为继承java.long.Thread类与实现java.long.Runnable接口,下面将分别对实现线程的两种方式进行讲解
1.继承Thread类
Thread类时java.long包中的一个类,Thread类的对象用来代表线程,通过继承Thread类创建、启动并执行以个线程的步骤如下:
- 创建一个继承Thread类的子类;
- 重写Thread类的run()方法;
- 创建线程类的一个对象;
- 通过线程类的对象调用start()方法启动线程(启动之后会自动调用重写的run()方法执行线程);
上面分别对以上4个步骤的实现进行介绍,首先启动一个新线程需要创建Thread实例,Thread类常用的两个构造方法如下:
- public Thread():创建一个新线程;
- public Thread(String ThreadName):创建一个名称为ThreadName的线程对象;
继承Thread类创建一个新线程的语法如下:
public class Demo extends Thread{
}
创建一个新线程后,如果需要操作创建好的新线程,那么需要使用Thread类提供的方法,Thread类的常用方法如下表:
方法 | 说明 |
---|---|
interrupt() | 中断线程 |
join() | 等待该线程终止 |
join(long millis) | 等待该线程终止的时间最长为millis毫秒 |
run() | 如果该线程是使用独立的Runnable对象构造的,则调用该Runnable对象的run()方法:否则,该方法不执行任何操作并返回。 |
setPriority(int newPriority) | 更改线程的优先级 |
sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) |
start() | 使该线程开始执行,java虚拟机调用该线程的run()方法 |
yield() | 暂停当前正在执行的线程对象,执行其他线程 |
当一个类继承Thread类后,就在线程类中重写run()方法,并将实现线程功能的代码写入run()方法中,然后调用Thread类的start()方法启动线程,线程启动之后会自动调用重写的run()方法执行线程。
Thread类对象需要一个任务来执行,任务是指线程在启动之后执行的工作,任务的代码写在run()方法中,run()方法必须使用以下语法格式:
public class Demo extends Thread{
public void run(){
}
}
注意:如果start()方法调用一个已经启动的线程,系统将抛出IIIegalThreadStateException异常
java虚拟机调用java程序的main方法时,就启动了主线程,如果程序员想启动其他线程,那么需要通过线程对象调用start()方法来实现,代码如下:
public static void main(String[] args) {
Thread th = new Thread();
th.start();
}
下面一个实例来讲解Thread类的用法,代码如下:
public class Demo extends Thread { // 指定类继承Thread类
private int count = 10;
public void run() { // 重写run()方法
while (true) {
System.out.print(count + " "); // 打印count变量
if (--count == 0) { // 使count变量自减,当自减为0时,退出循环
break;
}
}
}
public static void main(String[] args) {
Demo test = new Demo(); // 创建线程对象
test.start(); // 启动线程
}
}
返回结果:
2.实现Runnable接口
如果当前类不仅要继承其他类(非Thread类),还要实现多线程,那么该如何处理呢?继承Thread类肯定不行,因为java不支持多类继承,在这种情况下,只能通过当前类实现Runnable接口来继承创建Thread类对象
Object类的子类实现Runnable接口的语法如下:
public class Demo extends Object implements Runnable{
}
从javaAPI中可以发现,Thread类已经实现了Runnable接口,Thread类的run()方法正是Runnable接口中的run()方法的具体实现
实现Runnable接口的程序会创建一个Thread对象,并将Runnable对象与Thread对象相关联,Thread类中有以下两种构造方法:
- public Thread(Runnable target):创建新的Thread对象,以便将实现Runnable接口的对象target作为其运行对象
- public Thread(Runnable target,String name):创建新的Thread对象,以便将被指定名称的、实现Runnable接口的对象target作为其运行对象
使用Runnable接口启动新线程的步骤如下:
- 创建Runnable对象;
- 使用参数为Runnable对象的构造方法创建Thread对象;
- 调用start()方法启动线程;
通过Runnable接口创建线程时,首先需要创建一个实现Runnable接口的类,然后创建该类的对象,接下来使用Thread类中相应的构造方法创建Thread对象,最后使用Thread对象调用Thread类中的start()方法启动线程,实现Runnable接口创建线程的流程如下图:
下面的实例通过线程在GUI程序中实现了移动图标的功能
import java.awt.Container;
import java.net.URL;
import javax.swing.*;
public class Demo extends JFrame {
private JLabel jl = new JLabel(); // 声明JLabel对象
private static Thread t; // 声明线程对象
private int x = 0; // 声明可变化的横坐标
private Container container = getContentPane(); // 声明容器
public Demo() {
setBounds(300, 200, 250, 100); // 绝对定位窗体大小与位置
container.setLayout(null); // 使窗体不使用任何布局管理器
try {
// 获取本类同目录下图片的URL
//URL url = SwingAndThread.class.getResource("java.png");
Icon icon = new ImageIcon("src/java.png"); // 实例化一个Icon
jl.setIcon(icon); // 将图标放置在标签中
} catch (NullPointerException ex) { // 捕捉空指针异常
System.out.println("图片不存在,请将java.png拷贝到当前目录下!");
return; // 结束方法
}
jl.setBounds(10, 10, 200, 50); // 设置标签的位置与大小
t = new Thread(new Roll()); // 创建子类Roll类的线程对象
t.start(); // 启动线程
container.add(jl); // 将标签添加到容器中
setVisible(true); // 使窗体可见
setDefaultCloseOperation(EXIT_ON_CLOSE); // 设置窗体的关闭方式
}
class Roll implements Runnable { // 定义内部类,实现Runnable接口
public void run() {
while (x <= 200) { // 设置循环条件
// 将标签的横坐标用变量表示
jl.setBounds(x, 10, 200, 50);
try {
Thread.sleep(500); // 使线程休眠500毫秒
} catch (Exception e) {
e.printStackTrace();
}
x += 4; // 使横坐标每次增加4
if (x >= 200) {
x = 10; // 当图标到达标签的最右边时,使其回到标签最左边
}
}
}
}
public static void main(String[] args) {
new Demo(); // 实例化一个SwingAndThread对象
}
}
返回结果如下图: