第一种方式:继承Thread类,覆写run()方法
1、创建一个MyThread类,继承Thread类;
2、覆写run()方法,在run()方法内编写任务代码;
3、创建MyThread类,需要注意的是,如果想要给线程命名,需要在MyThread类中创建构造方法,在构造方法内调用父类Thread的有参构造
4、启动线程
/**
* 多线程的第一种创建方式
*/
public class EssayTest01 {
public static void main(String[] args) {
// 创建两个继承了Thread的类对象,并启动
MyThread t1 = new MyThread("线程1");
MyThread t2 = new MyThread("线程2");
// 启动线程
t1.start();
t2.start();
}
}
/**
* 创建一个类,继承Thread类,覆写run()方法
*/
class MyThread extends Thread {
/**
* 如果要给线程命名,这里需要调用Thread中的有参构造方法
*/
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " 这是多线程的第一种创建方式:运行第" + (i + 1) + "次");
}
}
}
第二种方式:实现Runnable接口,实现run()方法,再创建Thread对象
1、创建一个MyRunnable类,实现Runnable接口;
2、实现run()方法,在run()方法内编写任务代码;
3、创建MyRunnable类对象
4、创建Thread线程类对象,将MyRunnable对象作为参数传入
5、启动线程
/**
* 多线程实现方式2:实现Runnable接口
*/
public class EssayTest02 {
public static void main(String[] args) {
// 创建MyRunnable对象
MyRunnable mr = new MyRunnable();
// 创建线程对象并设置线程名,将MyRunnable对象作为参数传入
Thread t1 = new Thread(mr, "线程1");
Thread t2 = new Thread(mr, "线程2");
// 启动线程
t1.start();
t2.start();
}
}
/**
* 创建一个类,实现Runnable接口,实现run()方法
*/
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " 这是多线程的第二种创建方式:运行第" + (i + 1) + "次");
}
}
}
另外,可以使用匿名内部类+lambda表达式的方式实现,如下:
/**
* 多线程实现方式2:实现Runnable接口,用匿名内部类+lambda表达式
*/
public class EssayTest02 {
public static void main(String[] args) {
// 创建线程对象并设置线程名,将MyRunnable对象作为参数传入
Thread t1 = new Thread(() -> {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " 这是多线程的第二种创建方式:运行第" + (i + 1) + "次");
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " 这是多线程的第二种创建方式:运行第" + (i + 1) + "次");
}
});
// 启动线程
t1.start();
t2.start();
}
}
但需要注意的是,这样就不能让两个线程对同一资源进行操作了
第三种方式:实现Callable接口,实现call()方法,再创建FutureTask来管理后面创建的Thread对象
1、创建一个MyCallable类,实现Callable接口;
2、实现call()方法,在call()方法内编写任务代码;
3、创建MyCallable类对象
4、创建FutureTask对象,将MyCallable对象作为传入
5、创建Thread线程对象,将FutureTask对象作为参数传入
6、启动线程
需要注意的是,
(1)FutureTask泛型的类型要与call()方法的返回值,即Callable指定的泛型一致;
(2)使用FutureTask对象的get()方法获取线程的返回值,要在线程启动start()方法的后面,不然程序会干瞪眼,不会输出任何结果
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 多线程创建方式3:实现Callable接口,实现call()方法,再创建FutureTask来管理后面创建的Thread对象
*/
public class EssayTest03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建MyCallable对象
MyCallable mc = new MyCallable();
// 创建FutureTask对象,将MyCallable对象作为传入传入,FutureTask对象的泛型要与Callable的一致
FutureTask<String> ft = new FutureTask<String>(mc);
// 创建线程,将FutureTask作为参数传入
Thread t = new Thread(ft);
// 启动线程
t.start();
// 通过get()方法获取线程的返回值,注意要放在start()后面
System.out.println(ft.get());
}
}
/**
* 创建一个类,实现Callable接口,实现call()方法,注意call()方法的泛型指的是方法的返回值类型
*/
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " 这是多线程的第三种创建方式:运行第" + (i + 1) + "次");
}
return "success";
}
}
总结
第一种创建方式:逻辑简单,结构清晰,便于理解;但耦合高,每个对象都和MyThread直接关联,而且如果要设置线程名,还必须创建一个有参的构造方法
第二种创建方式:使用lambda表达,可以简化代码,解耦合;但如果涉及到多个线程对同一资源处理,就不能使用lambda表达式的方式了,另外线程没有返回值,无法知道线程运行结果
第三种创建方式:功能强大,可以返回线程运行的结果;但结构复杂,不易理解