java创建线程的方式
方法一:直接使用Thread
// 创建线程对象
Thread t = new Thread() {
public void run() {
// 要执行的任务
}
};
// 启动线程
t.start();
例如:
public static void main(String[] args) {
Thread t1 = new Thread("t1") {
@Override
// run 方法内实现了要执行的任务
public void run() {
System.out.println("hello");
}
};
t1.start();
}
输出:
方式二:使用Runnable配合Thread
把线程和任务(要执行的代码分开)
- Thread代表线程
- Runnable可运行的任务(线程要执行的代码)
Runnable runnable = new Runnable() {
public void run(){
// 要执行的任务
}
};
// 创建线程对象
Thread t = new Thread( runnable );
// 启动线程
t.start();
例如:
public static void main(String[] args) {
// 创建任务对象
Runnable task2 = new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();
}
结果:
java8以后可以使用lambda精简代码
// 创建任务对象
Runnable task2 = () -> log.debug("hello");
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();
方法三:FutureTask 配合 Thread
FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
public static void main(String[] args) {
try {
// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
System.out.println("hello");
return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
System.out.println("结果是:"+result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IjcaEgbR-1652779756165)(https://gitee.com/mengyuw20/cloudimage/raw/master/img/image-20220517171738250.png)]
Thread和Runnable的关系
我们先看下Runnable的源码:
public interface Runnable {
public abstract void run();
}
可以看到Runnable接口就这么简单。如果没有Thread类,那么Runnable接口就是普通得不能再普通的一个接口了,我们在代码中实现这个接口,也做不了任何事情!但由于得到Thread类的“青睐”,这个接口就变得不一般了!
那么Runnable接口得到Thread类的青睐,具体表现在哪呢?我们不妨先看看Thread类的定义
public class Thread implements Runnable{}
原来Thread是Runnable的一个实现类!!!以程序人的第一直觉,那Thread自然应该实现了run方法,我们继续在源码中搜寻!
@Override
public void run() {
if (target != null) {
target.run();
}
}
果不其然,Thread实现了run方法,并且有个判断,当target为null的时候什么也不做,否则执行target的run方法,target又是什么呢?
private Runnable target;
target也是一个Runnable对象,那这个私有的字段在哪里赋值的呢?我们继续寻找发现是在init方法里面进行赋值的,并且最终在构造函数中调用了init方法,我们看看构造函数的定义(Thread重载了多个构造函数)
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
总结一下:
- Runnable只是一个普通的接口。
- Thread实现了Runnable接口,并且实现了接口的run方法。
- Thread提供了重载的构造函数,接收Runnable类型的参数。在Thread重写的run方法中对调用了构造函数传入的Runnable实现类的run方法。
所以不管我们用哪种方式创建线程,都要实现或者重写run方法!并且这个run方法都是实现的Runnable接口中的方法。这个run方法就是我们自定义的需要在线程中处理的一些逻辑!
提供了重载的构造函数,接收Runnable类型的参数。在Thread重写的run方法中对调用了构造函数传入的Runnable实现类的run方法。
所以不管我们用哪种方式创建线程,都要实现或者重写run方法!并且这个run方法都是实现的Runnable接口中的方法。这个run方法就是我们自定义的需要在线程中处理的一些逻辑!