导言
线程的创建有三种方法,第一种方法是继承Thread类,第二种方法是实现Runnbale接口,第三种是实现Callable接口
目录
如何创建线程
第一种方法,继承 Thread 类
实例代码
/**
* TestThread1 类
* 操作人:小白
* 日期:2021/10/11
* 时间:14:41
* 第一个创建线程的方法:继承Thread类
* 1.继承 Thread 类
* 2.重写 run() 方法
* 3.创建对象
* 4.调用 start() 方法,开启线程
*/
public class TestThread1 extends Thread{
@Override
// 重写 run() 方法
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("I'm libra,I'm " + i + ".");
}
}
public static void main(String[] args){
// 创建对象
TestThread1 testThread1 = new TestThread1();
// 调用 start() 方法
testThread1.start();
for (int i = 0; i < 20; i++) {
System.out.println("I'm ragel,I'm " + i + ".");
}
}
}
运行后结果图
我们可以看到线程(Thread)创建后并没有被立即执行,而是先打印了几个主方法内的再去执行了它,因为线程创建了不一定立即执行,需要遵守CPU调度规则,如果只有一个CPU,也就是单核的情况,那就会以时间切片的形式使其达到同时执行的情况,其实本质上不是,但因为跳转的很快,我们可以认为其是。
如果我们不创建线程,用下面这样的方式去输出
运行结果
可以看到如果不创建线程,就会按顺序一步一步地输出,而创建了线程,就会遵守CPU调度规则,抢占式执行
第二种方法,
实现 Runnable 接口
实例代码
/**
* TestThread2 类
* 操作人:小白
* 日期:2021/10/11
* 时间:17:04
* 实现 Runnable 接口
*/
public class TestThread2 implements Runnable{
// 重写run()方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("I'm Libra,I'm " + i);
}
}
public static void main(String[] args) {
// 创建Runnable接口实现类对象
TestThread2 testThread2 = new TestThread2();
// 创建线程对象并传入实现类对象
Thread thread = new Thread(testThread2);
// 用线程对象去启动线程
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("I'm Ragel,I'm " + i);
}
}
}
运行结果图
实现 Runnable 接口和继承Thread类是一样的,原因是Thread类也实现了Runnable接口,那么自然而然的也就有这两个方法来实现创建线程了,但是我们推荐实现Runnable接口,因为Java中是单继承的,如果使用继承 Tread 类的话,就会出现OOP单继承局限性,会增加两个类之间的耦合性,会导致父类的改动直接影响子类,并且无法再次继承,因此我们推荐实现 Runnable 接口,完美解决了这个问题。
第三种方法,实现 Callable 接口
实例代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* TestCallable 类
* 操作人:小白
* 日期:2021/10/11
* 时间:19:34
* 实现 Callable 接口
* 1.实现 Callable 接口
* 2.重写 call() 方法
* 3.创建 FutureTask 对象
* 4.启动线程
*/
public class TestCallable implements Callable<String> {
private int tickt = 100;
@Override
public String call() throws Exception {
String result;
while (tickt > 0){
System.out.println(Thread.currentThread().getName() + "抢到了第 " + tickt-- + " 张票");
}
result = (String) "票已卖光。";
return result;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable mc = new TestCallable();
FutureTask<String> ft = new FutureTask<>(mc);
FutureTask<String> fe = new FutureTask<>(mc);
new Thread(ft).start();
new Thread(fe).start();
// 可以用 get() 获取返回值
String result = ft.get();
System.out.println(result);
}
}
运行结果
Callable 接口与 Runnable 接口对比
1.Callable 接口中,需要重写的方法是 call(),而 Runnable 接口中,需要重写的方法是 run()
2.Callable 接口有返回值,而 Runnable 接口没有
3.Callable 接口可以抛出异常,而 Runnable 接口没有
总结一下
有三种方法创建线程
1.继承 Thread 类,操作简单,但是会有OOP单继承局限性,代码耦合度较高
2.实现 Runnable 接口,操作较1复杂,但不具有OOP单继承局限性
3.实现 Callable 接口,操作复杂,但 call() 方法有返回值,可以抛出异常