线程的创建方式
1、继承Thread类实现
如何实现?
- 继承Thread类
- 重写run方法
- 创建线程对象
- 调用start()方法
/**
* @author Leah
* @Description:多线程创建方式一:继承Thread类实现
* @Title: ThreadDemo1
* @Package com.threadPool.Thread
* @date 2022-05-12 14:55
*/
public class ThreadDemo1 {
public static void main(String[] args) {
//3、new一个新线程对象
Thread t=new MyThread();
//4、调用start方法启动线程(执行的是run方法)
t.start();
for (int i=0;i<5;i++){
System.out.println("主线程执行输出"+i);
}
}
}
/**
* 1、定义一个线程类继承Thread
*/
class MyThread extends Thread{
/**
* 2、重写run方法,里面是定义线程以后要干啥
*/
@Override
public void run(){
for (int i=0;i<5;i++){
System.out.println("子线程执行输出"+i);
}
}
}
输出结果,主线程和子线程同时执行
优缺点:
- 优点 :代码简单
- 缺点 :
①继承 thread类,导致不能继承其他类,不便于拓展
②如果线程有执行结果是不可以直接返回的,run方法返回类型是void,
思考
为啥不直接调用run方法,而是调用start启动线程
- 直接调用run方法会当成普通方法执行,那就不会创建新的线程,还是当线程执行,只有一个main方法的线程
- 只有调用start方法才会启动一个新的线程执行
注意
如果主线程任务放在子线程之前了,那么也就相当于当线程的效果了
2、实现Runnable接口
步骤:
- 定义一个线程任务类实现Runnable 接口,实现run方法
The other way to create a thread is to declare a class that implements the Runnable interface.That class then implements the run method.
- 创建线程任务类,并交给thread 处理,调用线程对象的start()方法启动线程
An instance of the class can be allocated, passed as an argument when creating Thread, and started.
/**
* @author Leah
* @Description:多线程创建方式二:继承Thread类实现
* @Title: ThreadDemo1
* @Package com.threadPool.Thread
* @date 2022-05-12 14:55
*/
public class ThreadDemo2 {
public static void main(String[] args) {
//3、new一个任务对象
Runnable target=new MyRunnable();
//把任务对象交给Thread处理
Thread t=new Thread(target);
//4、调用start方法启动线程
t.start();
for (int i=0;i<5;i++){
System.out.println("主线程执行输出"+i);
}
}
}
/**
* 1、定义一个线程任务类,实现Runnable接口
*/
class MyRunnable implements Runnable{
/**
* 2、实现run方法,里面是定义线程以后要干啥(执行任务)
*/
@Override
public void run(){
for (int i=0;i<5;i++){
System.out.println("子线程执行输出"+i);
}
}
}
匿名内部类写法
public class ThreadDemo2Other {
public static void main(String[] args) {
//匿名内部类方式
new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<5;i++){
System.out.println("子线程1执行输出"+i);
}
}
}).start();
//lambda方式简化后
new Thread(()-> {
for (int i=0;i<5;i++){
System.out.println("子线程2执行输出"+i);
}
}).start();
//4、调用start方法启动线程
for (int i=0;i<5;i++){
System.out.println("主线程执行输出"+i);
}
}
输出结果,主线程和子线程同时执行
优缺点:
- 优点 :线程任务类只是实现接口,拓展性强
- 缺点 :
①编程多一层对象包装
②如果线程有执行结果是不可以直接返回的,run方法返回类型是void,不适合需要返回线程执行结果的业务场景
那么如何解决上面两种方式无返回值的问题呢?
那就用到第三种方式
3、利用Callable、FutureTask接口实现
步骤:
1、得到任务对象
①定义一个任务类,实现Callable接口,重写call方法(任务方法),封装要做的事
②用FutureTask把Callable对象封装成线程任务对象
2、把线程任务对象交给线程对象处理,并启动线程
3、执行完毕后,通过FutureTask的get方法获取执行返回结果
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* @author Leah
* @Description: 实现Callable接口,结合FutureTask完成
* @Title: ThreadDemon3
* @Package com.threadPool.Thread
* @date 2022-05-12 16:57
*/
public class ThreadDemon3 {
public static void main(String[] args) {
//创建Callable任务对象,并把Callable任务对象交给FutureTask对象
//FutureTask对象的作用1:是Runnable的对象(实现了Runnable接口),可以交给Thread
//FutureTask对象的作用2:可以在线程执行完毕时调用其get方法得多线程执行完的结果
FutureTask<String> futureTask1 = new FutureTask<>(new MyCallable("1"));
//交给线程处理,并启动线程
new Thread(futureTask1).start();
FutureTask<String> futureTask2 = new FutureTask<>(new MyCallable("2"));
new Thread(futureTask2).start();
try {
//如果futureTask1任务没有执行完毕,这里的代码会等待,知道线程跑完才提取结果
String s1 = futureTask1.get();
System.out.println(s1);
} catch (Exception e) {
e.printStackTrace();
}
try {
String s1 = futureTask2.get();
System.out.println(s1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 1、定义一个任务类,实现Callable接口 应该申明线程任务执行完毕后的返回结果的数据类型
*/
class MyCallable implements Callable<String>{
private String value;
public MyCallable(String value) {
this.value = value;
}
/*
*重写call方法(任务方法),封装要做的事
* @throws Exception
*/
@Override
public String call() throws Exception {
for (int i=0;i<5;i++){
System.out.println("子线程"+value+"执行输出"+i);
}
return "子线程返回值"+value;
}
}
结果:
子线程1执行输出0
子线程1执行输出1
子线程1执行输出2
子线程1执行输出3
子线程2执行输出0
子线程1执行输出4
子线程2执行输出1
子线程2执行输出2
子线程2执行输出3
子线程返回值1
子线程2执行输出4
子线程返回值2
优缺点:
- 优点 :可拓展,可得到执行结果返回值
- 缺点 :代码复杂了点