1. 多线程的创建的三种方式
多线程的创建主要有三种方式。
- 继承Thread类(将任务和线程合并在一起)
- 实现Runnable接口(将任务和线程分开了)
- 实现Callable接口(利用FutureTask执行任务)
1.1. 继承Thread类
package com.asleepyfish;
/**
* @Author: asleepyfish
* @Date: 2022-02-28 21:44
* @Description: 继承Thread类
*/
public class InheritThread extends Thread {
private int num = 5;
private final String name;
public InheritThread(String name) {
this.name = name;
}
@Override
public void run() {
while (num > 0) {
System.out.println(name + "拿了第" + num-- + "张票");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
InheritThread thread1 = new InheritThread("小明");
InheritThread thread2 = new InheritThread("小华");
thread1.start();
thread2.start();
}
}
第一次运行结果:
小华拿了第5张票
小明拿了第5张票
小明拿了第4张票
小华拿了第4张票
小华拿了第3张票
小明拿了第3张票
小明拿了第2张票
小华拿了第2张票
小明拿了第1张票
小华拿了第1张票
===================
第二次运行结果:
小华拿了第5张票
小明拿了第5张票
小明拿了第4张票
小华拿了第4张票
小华拿了第3张票
小明拿了第3张票
小华拿了第2张票
小明拿了第2张票
小华拿了第1张票
小明拿了第1张票
多线程的执行是乱序执行的,每次的结果也都是随机的。
1.2. 实现Runnable接口
package com.asleepyfish;
/**
* @Author: asleepyfish
* @Date: 2022-02-28 21:59
* @Description: 实现Runnable接口
*/
public class RunnableThread implements Runnable {
private int num = 5;
@Override
public void run() {
while (num > 0) {
System.out.println(Thread.currentThread().getName() + "拿了第" + num-- + "张票");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 这里还是new了两个RunnableThread对象,两个线程并不是操作同一个资源,没有实现资源共享
new Thread(new RunnableThread(), "小明").start();
new Thread(new RunnableThread(), "小华").start();
// 如果只new了一个对象去执行会引发并发线程安全问题
// RunnableThread runnableThread = new RunnableThread();
// new Thread(runnableThread, "小红").start();
// new Thread(runnableThread, "小强").start();
}
}
第一次运行结果:
小明拿了第5张票
小华拿了第5张票
小华拿了第4张票
小明拿了第4张票
小明拿了第3张票
小华拿了第3张票
小华拿了第2张票
小明拿了第2张票
小华拿了第1张票
小明拿了第1张票
================
第二次运行结果:
小明拿了第5张票
小华拿了第5张票
小华拿了第4张票
小明拿了第4张票
小明拿了第3张票
小华拿了第3张票
小华拿了第2张票
小明拿了第2张票
小明拿了第1张票
小华拿了第1张票
1.3 实现Callable接口
package com.asleepyfish;
import java.util.concurrent.*;
/**
* @Author: asleepyfish
* @Date: 2022-03-01 21:57
* @Description: 实现Callable接口
*/
public class CallableThread implements Callable<String> {
private int step = 10;
@Override
public String call() {
String result;
while (step > 0) {
System.out.println("还剩:" + step + "步");
step--;
}
result = "走完了";
return result;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableThread mc = new CallableThread();
// 创建执行服务
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 提交执行
Future<String> submit = executorService.submit(mc);
// 获取结果
String result = submit.get();
System.out.println(result);
// 关闭服务
executorService.shutdown();
}
}
还剩:10步
还剩:9步
还剩:8步
还剩:7步
还剩:6步
还剩:5步
还剩:4步
还剩:3步
还剩:2步
还剩:1步
走完了
2. 继承Thread和实现Runnable的区别
参考: Java多线程学习
继承Thread类,则不适合资源共享。实现Runable接口的话,很容易实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势
- 适合多个相同的程序代码的线程去处理同一个资源
- 可以避免java中的单继承的限制
- 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
- 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。