文章目录

    • 方式一:继承 Thread 类
      • 提问:这个run()方法究竟是从哪里来的呢?
    • 为什么要有第二种启动线程的方式
    • 方式二:实现 Runnable 接口
    • 总结:

 

方式一:继承 Thread 类

继承 Thread 类创建一个新的线程语法:

public class ThreadTest extends Thread{
	@Override			
	public void run() {
		// TODO Auto-generated method stub
		super.run();
	}
}

别怕,我们一层层分析。
当我们的自定义类继承了 Thread 类后,必须要实现run()方法。要算是一个有意义的线程
为什么这么说?
简单理解:
把线程要做的事情放到run()方法里,当线程启动后,会直接执行run()方法里面的所有代码。
好,那么问题来了。

提问:这个run()方法究竟是从哪里来的呢?

问得很好,run() 方法上方有一个@Override 标记,代表它是一个抽象方法,我们都知道子类是必须要继承父类 Thread的抽象方法的,而 Thread 又实现了Runnable 接口,接口中的方法全都是抽象方法,且不能有方法体,所以说,知道怎么来的了吧!

要想让线程能够得到执行,我们需要启动线程,这时候线程才能拿到cpu时间片从而才能启动
启动线程语法:

public static void main(String[] args){
	new ThreadTest().start();
}

例1:

public class ThreadTest extends Thread {		//继承 Thread 类
	
	private int count = 10;			
	public void run() {					//重写 run() 方法
		while(true) {
			System.out.print(count + " ");		//打印 count 变量
			if(--count == 0) {					//使 count 变量自减,当自减为 0 时,退出循环
				return;
			}
		}
	}
	
	public static void main(String[] args) {
		new ThreadTest().start();
	}

}

class A extends Thread{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
	}
}

结果为:

10 9 8 7 6 5 4 3 2 1 

重要结论:
如果不调用start() 方法,线程永远都不会启动,在主方法没有调用start()方法之前,Thread 对象只是一个实例,而不是一个真正的线程。

为什么要有第二种启动线程的方式

学习第二种方式之前,我们需要知道,为什么有1种实现了以后还要搞第二种不是浪费时间吗,不,开发者可比我们聪明多了,针对不同的情景,我们会使用不同的方式去实现。

具体原因:
如果程序员需要继承其他类(非Thread 类),因为Java不能支持多继承,此时还要使当前类实现多线程,那么就可以通过 Runnable 接口来实现。

所以我们知道了第二种方式诞生原因后,就一起来学习吧

方式二:实现 Runnable 接口

语法:

public class Thread extends Object implements Runnable

其实 Thread 类它实现了 Runnable 对象,其中的 run()方法正是对 Runnable 接口中的 run() 方法的具体实现。

实现 Runnable 接口创建线程的流程图
Java 实现线程的两种方式_Runnable 接口

例1:

import java.awt.Container;
import java.net.URL;
import javax.swing.*;

public class SwingAndThread extends JFrame {
	private JLabel jl = new JLabel();		//声明 JLabel 对象
	private static Thread t;				//声明线程对象
	private int count = 0;					//声明计数变量
	private Container container = getContentPane();			//声明容器
	
	public SwingAndThread() {
		setBounds(300,200,250,100);   	//绝对定位窗体大小与位置
		container.setLayout(null);      //使窗体不适用任何布局管理器
		URL url = SwingAndThread.class.getResource("1.png");  //获取图片的URL
		Icon icon = new ImageIcon(url);		//实例化一个 Icon
		jl.setIcon(icon);    //将图标放置再标签中
		jl.setHorizontalAlignment(SwingConstants.LEFT);		//设置图片在标签的最左方
		jl.setBounds(10, 10, 200, 50);  	//设置标签的位置与大小
		jl.setOpaque(true);  	
		t = new Thread(new Runnable() {				//定义匿名内部类,该类实现 Runnable 接口
			@Override
			public void run() {						//重写run() 方法
				while(count <= 200) {				//设置循环条件
					jl.setBounds(count, 10, 200, 50);  //将标签的横坐标用变量表示
					try {
						Thread.sleep(1000);			//将线程休眠 1000 毫秒
					}catch (Exception e) {
						e.printStackTrace();
					}
					count += 30;				//使横坐标每次增加4
					if(count >= 200) {		//当图标到达标签的最右边时,使其回到标签最左边
						count=10;
					}
				}
			}
		});
		t.start();			//启动线程
		container.add(jl);	//将标签添加到容器中
		setVisible(true);  	//设置窗体可见
		setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);	//设置窗体关闭方式
	}
	
	public static void main(String[] args) {
		new SwingAndThread();		//实例化一个 SwingAndThread 对象
//		System.out.println(SwingAndThread.class.getResource(""));
	}

}

运行结果(有水印望各位请体谅理解):

Java 实现线程的两种方式_Thread_02

注意:这里我要讲解下面这行代码

URL url = SwingAndThread.class.getResource("1.png");

当时我有卡在这里,因为不知道图片放在哪里,getResource() 方法才能获取到。后面百度了下,才找到了解决的方法:
可以看到,我在 main() 方法里注释了一行代码:

System.out.println(SwingAndThread.class.getResource(""));

这行代码,是用来获取当前当前项目的运行路径的。结果为:
Java 实现线程的两种方式_Runnable 接口_03
所以能够得出,图片肯定放在bin目录下才能获取到。
Java 实现线程的两种方式_实现线程的两种方式_04

总结:

  • run() 方法用来存放 线程执行逻辑
  • 只有调用 start() 方法才会产生线程实体,并且运行。