【前言】Java中的多线程确实很难,我认为是JavaWeb开发过程中最难的一个知识点,我以前也是感觉学会了,但是真正有多线程的需求却不知道怎么下手,实际上还是对多线程这块知识了解不深刻,不知道多线程api的应用场景,不知道多线程的运行流程等等。话不多说,我们直接上干货,篇幅有点长,小伙伴们坐稳了~
进程:
线程:
多线程:
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
为什么需要使用多线程?
多线程能实现的都可以用单线程来完成,那单线程运行的好好的,为什么java要引入多线程的概念呢?
多线程的好处:
为什么很多人都觉得多线程很难,那么它难在哪里呢?
个人认为:单线程只有一条执行线,过程容易理解,可以在大脑中清晰的勾勒出代码的执行流程
多线程却是多条线,而且一般多条线之间有交互(多条线之间需要通信),所以总结了以下几个点:
1: 多线程的安全问题
2:线程资源宝贵,依赖线程池操作线程,线程池的参数设置问题
3:多线程执行是动态的,同时的,难以追踪过程
【java多线程的基本使用】定义任务、创建和运行线程
任务:线程的执行体。也就是我们的核心代码逻辑
定义任务
Thread实现任务的局限性
Runnable和Callable解决了Thread的局限性,但是Runbale相比Callable有以下的局限性
几种定义线程的方式,案例如下:
class T extends Thread {
@Override
public void run() {
log.info("继承Thread的任务");
}
}
class R implements Runnable {
@Override
public void run() {
log.info("实现Runnable的任务");
}
}
class C implements Callable {
@Override
public String call() throws Exception {
log.info("实现Callable的任务");
return "成功";
}
}
创建线程的方式
启动线程的方式
// 启动继承Thread类的任务
new T().start();
// 启动继承Thread匿名内部类的任务 可用lambda优化
Thread t = new Thread(){
@Override
public void run() {
log.info("我是Thread匿名内部类的任务");
}
};
// 启动实现Runnable接口的任务
new Thread(new R()).start();
// 启动实现Runnable匿名实现类的任务
new Thread(new Runnable() {
@Override
public void run() {
log.info("我是Runnable匿名内部类的任务");
}
}).start();
// 启动实现Runnable的lambda简化后的任务
new Thread(() -> log.info("我是Runnable的lambda简化后的任务")).start();
// 启动实现了Callable接口的任务 结合FutureTask 可以获取线程执行的结果
FutureTask target = new FutureTask<>(new C());
new Thread(target).start();
log.info(target.get());
【上下文切换】
多核cpu下,多线程是并行工作的,如果线程数多,单个核又会并发的调度线程,运行时会有上下文切换的概念
cpu执行线程的任务时,会为线程分配时间片,以下几种情况会发生上下文切换。
当发生上下文切换时,操作系统会保存当前线程的状态,并恢复另一个线程的状态,jvm中有块内存地址叫程序计数器,用于记录线程执行到哪一行代码,是线程私有的。
yield()方法会让运行中的线程切换到就绪状态,重新争抢cpu的时间片,争抢时是否获取到时间片看cpu的分配。
代码如下
// 方法的定义
public static native void yield();
Runnable r1 = () -> {
int count = 0;
for (;;){
log.info("---- 1>" + count++);
}
};
Runnable r2 = () -> {
int count = 0;
for (;;){
Thread.yield();
log.info(" ---- 2>" + count++);
}
};
Thread t1 = new Thread(r1,"t1");
Thread t2 = new Thread(r2,"t2");
t1.start();
t2.start();
万字图解Java多线程,不信你学不会! - 知乎
【ThreadPoolTaskExecutor】 SpringBoot 的线程池的使用__Peko_的博客-CSDN博客_threadpooltaskexecutor线程池使用
ThreadPoolTaskExecutor_小甄笔记的博客-CSDN博客_threadpooltaskexecutor