概述
- Java 语言的 JVM 允许程序运行多个线程,它通过
java.lang.Thread
类来体现。 - Thread 类的特性:
-
- 每个线程都是通过某个特定 Thread 对象的 run() 方法来完成操作的,经常 把 run() 方法的主体称为
线程体
。 - 通过该 Thread 对象的 start() 方法来启动这个线程,而非直接调用 run() 。
- 每个线程都是通过某个特定 Thread 对象的 run() 方法来完成操作的,经常 把 run() 方法的主体称为
1 创建线程的方式
1.1 概述
- JDK 5 之前创建线程的两种方式:
-
- ① 继承Thread类。
- ② 实现Runnable接口。
1.2 继承 Thread 类
- 方法介绍
方法名 | 说明 |
void run() | 在线程开启后,此方法将被调用执行 |
void start() | 使此线程开始执行,Java虚拟机会调用run方法() |
- 实现步骤:
-
- ① 定义子类继承 Thread 类。
- ② 子类中重写 Thread 类中的 run 方法。
- ③ 创建 Thread 子类对象,即创建了线程对象。。
- ④ 调用线程对象 start 方法:启动线程,调用 run 方法。
- 代码演示:
-
package Thread.thread; /* 关于线程入门 方式1(类实现类) 打印数字0~100 线程交替运行 * */ //继承Thread接口 public class Mythread extends Thread{ //重写run方法 //run()是来封装被线程执行的代码 @Override public void run() { //super.run(); //使用多线程打印1~100 super.run(); for (int i = 0; i < 100; i++) { //可以使用currentThread()获取当前线程的对象 //System.out.println("i = " + i); System.out.println("当前线程为:"+Thread.currentThread().getName()+":"+i); } } public static void main(String[] args) { //main方法是该程序的主线程 //创建自定义多线程对象 Mythread mt1 =new Mythread(); Mythread mt2 =new Mythread(); //mt1.run(); //mt2.run(); //mt1.start(); //mt2.start(); //线程的命名 Thread t1 = new Thread(mt1, "线程1"); Thread t2 = new Thread(mt2, "线程2"); //启动线程 t1.run(); //run方法默认使用main线程执行(main线程最顶级) t2.run(); /*启动线程有run方法和start方法两种 * 但要区别run方法与start方法的区别 * run():封装线程的代码,直接调用,相当于普通方法的调用,并没有开启线程 * start():启动线程;然后由JVM调用此线程的run()方法 * */ t1.start(); //start方法为开辟新线程Thread-0 t2.start(); //Thread-1 } }
1.3实现 Runnable 接口
- Thread构造方法
- 实现步骤:
-
- ① 定义子类,实现 Runnable 接口。
- ② 子类中重写 Runnable 接口中的 run 方法。
- ③ 通过 Thread 类含参构造器创建线程对象。
- ④ 将 Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造器中。
- ⑤ 调用 Thread 类的 start 方法:开启线程,调用 Runnable 子类接口的 run 方法。
- 示例:
package Thread.thread; /* *方式2(类实现接口) * 实现Runnable接口 * * */ public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() +":"+ i); } } public static void main(String[] args) { //创建自定义线程对象 MyRunnable mr = new MyRunnable(); //Thread(Runnable target String name) Thread t1 = new Thread(mr, "线程1"); Thread t2 = new Thread(mr, "线程2"); Thread t3 = new Thread(mr, "线程3"); //启动线程 t1.start(); t2.start(); t3.start(); } }
1.3.实现Callable接口
- 方法介绍
-
方法名
说明
V call()
计算结果,如果无法计算结果,则抛出一个异常
FutureTask(Callable callable)
创建一个 FutureTask,一旦运行就执行给定的 Callable
V get()
如有必要,等待计算完成,然后获取其结果
- 实现步骤
-
- 定义一个类MyCallable实现Callable接口
- 在MyCallable类中重写call()方法
- 创建MyCallable类的对象
- 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
- 创建Thread类的对象,把FutureTask对象作为构造方法的参数
- 启动线程
- 再调用get方法,就可以获取线程结束之后的结果。
- 代码演示:
-
package Thread.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /* * Callable接口 * 特殊情况时使用 * */ public class MyCallable implements Callable { //需要重写call方法 @Override public Object call() throws Exception { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"-"+i); } return "结束"; } public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable mc=new MyCallable(); //创建几个线程就需要几个对象 FutureTask task1 = new FutureTask(mc); FutureTask task2 = new FutureTask(mc); FutureTask task3 = new FutureTask(mc); Thread t1 = new Thread(task1, "线程1"); Thread t2 = new Thread(task2, "线程2"); Thread t3 = new Thread(task3, "线程3"); t1.start(); t2.start(); t3.start(); //打印call的返回值 Object o = task1.get(); System.out.println(o); } }
2.继承方式和实现方式的对比
- 实现Runnable、Callable接口
-
- 好处:
-
-
- 避免了单继承的局限,扩展性强,实现该接口的同时还可以继承其他的类
- 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源
-
-
- 缺点: 编程相对复杂,不能直接使用Thread类中的方法
- 继承Thread类
-
- 好处: 编程比较简单,可以直接使用Thread类中的方法
- 缺点: 可以扩展性较差,不能再继承其他的类