Springboot启动过程详解_springboot启动流程-优快云博客
这个之后在看
预备知识:
为什么需要单继承?
- 避免复杂性:如果允许一个类从多个类继承,可能会引发“菱形继承问题”或其他复杂性。例如,两个父类可能有同名的方法,这时子类会不知道该继承哪个版本,增加了设计的复杂度。
- 清晰性:单继承确保了类结构简单、层次清晰,减少了冲突和混淆。
多态和接口的作用
虽然在 单继承 机制中,一个类只能继承一个父类,但 Java 提供了 接口(interface
),允许类实现多个接口,这在某种程度上弥补了单继承的限制,提供了更大的灵活性。
例如,一个类可以继承一个父类,并实现多个接口
1,继承Thread类
继承Thread类,重写run方法(不推荐,因为java的单继承局限性)
2,实现Runnable接口
方式一:直接实现Runnable接口(避免单继承的局限性,方便共享资源,推荐使用)
方式二:匿名内部类
匿名内部类本质上也是一个类实现了Runnable接口,重写了Thread类的run方法,只不过这个类没有名字(所以是匿名),直接作为参数传入Thread类
public class Anonymous {
/*
* 创建步骤如下:
* 匿名内部类本质上也是一个类实现了Runnable接口,重写了run方法,只不过这个类没有名字,直接作为参数传入Thread类
*
* 调用示例:
* //开启10个线程
* for (int i = 0; i < 10; i++) {
* Anonymous anonymous =new Anonymous();
* anonymous.myRun();
* }
*
* */
public void myRun(){
new Thread(new Runnable() {
@Override
public void run() {
doSomething();
}
}).start();
}
/**
* 需要处理的任务
* */
private void doSomething(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行" + i);
}
}
}
3,实现Callable接口
* 创建步骤如下:
* 1,定义实现Callable<V>接口的实现类,实现call方法,这个方法是线程执行体
* 2,创建Callable<V>实现类的实例,借助FutureTask得到线程执行的返回值
* 3,将FutureTask的实例,作为Thread的target来创建Thread类
* 4,调用start方法,开启线程
*
* 调用示例:
* Callable<String> tc = new CallableImpl();
* FutureTask<String> task = new FutureTask<>(tc);
* new Thread(task).start();
* try {
* System.out.println(task.get());
* } catch (InterruptedException | ExecutionException e) {
* e.printStackTrace();
* }
*
public class CallableImpl implements Callable<String> {
/*
* 创建步骤如下:
* 1,定义实现Callable<V>接口的实现类,实现call方法,这个方法是线程执行体
* 2,创建Callable<V>实现类的实例,借助FutureTask得到线程执行的返回值
* 3,将FutureTask的实例,作为Thread的target来创建Thread类
* 4,调用start方法,开启线程
*
* 调用示例:
* Callable<String> tc = new CallableImpl();
* FutureTask<String> task = new FutureTask<>(tc);
* new Thread(task).start();
* try {
* System.out.println(task.get());
* } catch (InterruptedException | ExecutionException e) {
* e.printStackTrace();
* }
*
* 说明:
* 1.与使用Runnable相比, Callable功能更强大些
* 2.实现的call()方法相比run()方法,可以返回值
* 3.方法可以抛出异常
* 4.支持泛型的返回值
* 5.需要借助FutureTask类,比如获取返回结果
* Future接口可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
* FutureTask是Futrue接口的唯一的实现类
* FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
*
* */
private int ticket = 5;
@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(doSomething());
}
return "出票任务完成";
}
public String doSomething() {
String result = "";
if (this.ticket > 0) {
result = "出票成功,ticket=" + this.ticket--;
} else {
result = "出票失败,ticket=" + this.ticket;
}
return result;
}
}
1. Callable
接口:
Callable
是 Java 中一个更强大的接口,相比于 Runnable
,它的主要特点是:
- 可以返回结果:
Callable
接口中的call()
方法可以返回一个结果,而Runnable
中的run()
方法是void
,不能返回值。 - 可以抛出异常:
Callable
的call()
方法可以抛出异常,而Runnable
的run()
方法不允许抛出异常。 - 支持泛型:
Callable<V>
可以返回指定类型V
的结果。
在代码中,CallableImpl
实现了 Callable<String>
,因此 call()
方法返回一个 String
类型的结果。
2. FutureTask
:
FutureTask
是 Java 中用于包装Callable
或Runnable
的类,它既可以作为一个Runnable
被线程执行,也可以通过它的get()
方法来获取Callable
返回的结果。FutureTask
实现了Future
接口,可以用来查询任务的状态、取消任务或者获取任务的执行结果。
3. 任务逻辑:
CallableImpl
类模拟了一个简单的出票逻辑。call()
方法会循环 10 次调用doSomething()
方法,每次根据剩余票数判断出票是否成功,并返回一个字符串。- 任务完成后,
call()
方法返回一个字符串结果"出票任务完成"
。
调用示例:
- 第1步:
Callable<String> tc = new CallableImpl();
创建一个CallableImpl
实例。(任务内容) - 第2步:
FutureTask<String> task = new FutureTask<>(tc);
使用FutureTask
将Callable
包装起来。FutureTask
可以同时作为一个Runnable
被线程执行,并且能够返回任务结果。(任务里面放内容tc) - 第3步:
new Thread(task).start();
创建并启动一个线程,线程的任务是执行Callable
中的call()
方法。 - 第4步:
task.get();
通过FutureTask
的get()
方法获取任务执行的结果。如果任务没有完成,它会阻塞直到结果可用。
-------------------------------------------------------------------------------
4,创建线程池********
Executors工具类实现(很方便)
Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的
/*
* 创建步骤如下:
* 1,定义Runnable接口的实现类,或者定义(继承Runnable接口的类)的实现类,并且实现run方法,这个方法是线程执行体
* 2,创建一个自定义线程个数的线程池
* 3,实例化Runnable接口的实现类
* 4,将3步的实例,作为线程池实例的execute方法的command参数,开启线程
* 5,关闭线程池
*
* 调用示例:
* ExecutorService pool = Executors.newFixedThreadPool(2);
* ThreadPool threadPool = new ThreadPool("AA");
* ThreadPool threadPoo2 = new ThreadPool("BB");
* pool.execute(threadPool);
* pool.execute(threadPoo2);
* pool.shutdown();
*
* 说明:
* 示例中创建的是2个线程的线程池
* execute方法是开启线程方法,实参要求是实现Runnable的类。所以,继承Thread类的子类也可以以线程池的方式开启线程
public class ThreadPool implements Runnable {
/*
* 创建步骤如下:
* 1,定义Runnable接口的实现类,或者定义(继承Runnable接口的类)的实现类,并且实现run方法,这个方法是线程执行体
* 2,创建一个自定义线程个数的线程池
* 3,实例化Runnable接口的实现类
* 4,将3步的实例,作为线程池实例的execute方法的command参数,开启线程
* 5,关闭线程池
*
* 调用示例:
* ExecutorService pool = Executors.newFixedThreadPool(2);
* ThreadPool threadPool = new ThreadPool("AA");
* ThreadPool threadPoo2 = new ThreadPool("BB");
* pool.execute(threadPool);
* pool.execute(threadPoo2);
* pool.shutdown();
*
* 说明:
* 示例中创建的是2个线程的线程池
* execute方法是开启线程方法,实参要求是实现Runnable的类。所以,继承Thread类的子类也可以以线程池的方式开启线程
*
* */
String name;
public ThreadPool(String name) {
this.name = name;
}
@Override
public void run() {
doSomething();
}
/**
* 需要处理的任务
* */
private void doSomething() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行" + i + ",name=" + this.name);
}
}
}
示例流程:
-
定义
Runnable
接口的实现类:ThreadPool
类实现了Runnable
接口,因此可以用来创建并发任务。
-
创建线程池:
- 使用
Executors.newFixedThreadPool(2)
创建了一个具有固定大小为 2 的线程池,这意味着线程池中最多同时运行两个线程。
- 使用
-
实例化
ThreadPool
类(实现了Runnable
接口):ThreadPool threadPool = new ThreadPool("AA");
实例化了一个ThreadPool
对象,并传递了名字 "AA"。ThreadPool threadPoo2 = new ThreadPool("BB");
同样创建了另一个名为 "BB" 的ThreadPool
实例。
-
将(实例化对象)任务提交给线程池执行:
pool.execute(threadPool);
将第一个ThreadPool
实例提交给线程池,线程池将分配一个线程来执行该任务。pool.execute(threadPoo2);
提交第二个任务,线程池将分配另一个线程来并行执行这个任务。
-
关闭线程池:
pool.shutdown();
在所有提交的任务执行完毕后关闭线程池,不再接收新的任务。
doSomething()
方法:
- 这是
ThreadPool
类中的实际任务,模拟执行了一个简单的循环任务。每次循环都会输出当前线程的名称以及任务的执行进度。
ThreadPoolExecutor执行Runnable任务(以下见下面那个链接)
ThreadPoolExecutor执行Callable任务(和runable区别就是都要用feature,比runable复杂一丢丢)
Java六种常用线程创建执行方法_java 线程执行-优快云博客
FutureTask
和 Future
在 Java 中都用于并发编程,尤其是在执行异步任务时获取任务的执行结果。它们有一些相似之处,但也有明显的区别。
1. Future 接口
Future
是一个接口,它的主要功能是表示异步计算的结果。它提供了一些方法来检查任务的状态、获取结果、取消任务等。
关键方法:
get()
: 获取任务的执行结果。如果任务没有完成,这个方法会阻塞,直到结果可用。cancel(boolean mayInterruptIfRunning)
: 尝试取消任务。isDone()
: 检查任务是否已经完成。isCancelled()
: 检查任务是否已经被取消。
适用场景:
Future
主要用于提交给 线程池 的任务,线程池会返回一个 Future
对象,用户可以使用这个 Future
来获取任务执行的状态和结果。
示例:
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "任务完成";
});
try {
// 阻塞等待任务执行完成并获取结果
String result = future.get();
System.out.println(result); // 输出 "任务完成"
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
2. FutureTask 类
FutureTask
是 Future
接口的一个具体实现类,除了具备 Future
的所有功能之外,它还实现了 Runnable
接口,意味着它可以作为一个 任务 被线程执行。这使得它既是一个 Runnable
,可以在线程中执行任务,又是一个 Future
,可以获取任务的执行结果。
特性:
FutureTask
可以包装一个Callable
或Runnable
,然后通过Thread
或者Executor
来执行。- 它既可以用来获取任务执行的结果,又可以通过
Runnable
来被线程直接运行。
适用场景:
FutureTask
适用于需要创建一个可以返回结果的任务,并手动控制任务的执行,而不仅仅依赖线程池。
示例:
Callable<String> callable = () -> {
Thread.sleep(1000);
return "任务完成";
};
// 用 FutureTask 包装 Callable
FutureTask<String> futureTask = new FutureTask<>(callable);
// 将 FutureTask 作为 Runnable 放入线程中执行
new Thread(futureTask).start();
try {
// 阻塞等待任务执行完成并获取结果
String result = futureTask.get();
System.out.println(result); // 输出 "任务完成"
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
区别:
-
功能上:
Future
是一个接口,表示异步计算的结果,可以用来监控任务状态、取消任务或获取任务结果。FutureTask
是Future
的实现类,同时实现了Runnable
接口,因此它既可以当作任务执行,也可以用来获取任务结果。
-
使用方式:
Future
通常是通过 线程池(如ExecutorService
)提交任务后返回的结果。FutureTask
可以独立使用,既可以提交给线程池执行,也可以直接通过Thread
来执行。
-
任务的执行:
Future
本身不能执行任务,它只是一个结果的占位符。FutureTask
本身是Runnable
,可以直接被线程执行。
总结:
Future
是一个接口,主要用于获取异步任务的结果和状态。FutureTask
是一个具体的实现,既能表示异步任务的结果,还能作为一个可执行的任务被线程执行。