线程,进程
什么是进程?
cpu从硬盘中读取一段程序到你内存中,该执行程序的实例就叫做进程.
什么是线程?
线程是程序执行的最小单位,在一个进程中,可以有个多个不同的线程,同时执行。
并行,并发
什么是并行?
多个水龙头同时流水。
什么是并发?
多个水龙头交错流水。
为什么要用多线程?
场景1:假如你要盖一个房子,从选材料,打地基,构建骨架,装修...这些都要你一个人完成,会非常非常耗时间。串行
什么是多线程?
把上面的操作交给多个人去处理,模块细化,分工合作,从而提高工作效率。并行
场景2:假如你要开发一个音乐软件,如果这个音乐软件是单线程的,那么他就只能专注于做一件事情,比如说你想在听歌时打开评论,那么就必须要停掉播放器,才能去打开评论,所以这是一件很不合乎常理的事情。
如果我们引入多线程,那么这件事情就很好解决。首先模块细化,播放器模块交给播放线程。评论模块交给评论线程,下载模块交给下载线程。这样无形之间就提高了程序的健壮性。
串行,并行
串行:单线程执行,代码从上向下执行,执行效率非常低。
并行:多个线程一起执行,效率比较高。
多线程一定就快吗?
拿盖房子来说,一个人要两年,10个人可能需要1年,30个人可能需要半年....
照这么算的话,1w个人是不是几秒就能盖一个房子?
显然是不合乎常理的,较多的线程会使cpu频繁切换上下文,从而导致性能损失. 1w个人盖房子,你光是通知每个人做什么就需要很多的时间,更别说管理起这1w个人了。所以多线程不是越多越好
cpu调度
如果开启了上千个线程,而服务器是8核,那么多线程会在我们这些cpu上做上下文切换。
上下文切换
从当前任务切换到,另外的任务.
多线程应用场景
- 客户端开发
- 异步发送
- 比较耗时的操作可以采用多线程
- 多线程下载
准备好了吗~干货来了
多线程创建方式
- 继承
Thread
类创建线程 - 实现
Runnable
接口 - 匿名内部类创建
lambda
表达式创建- 使用
callable
和Future
创建线程 - 使用
线程池
异步注解
继承Thread类实现
实现流程
- 编写一个类
- 继承Thread类
- 重写run方法
- 创建实例
- 调用
start
方法启动线程
这里需要注意的是虽然重写的是run方法但是启动线程必须使用,start方法。
package thread;
import java.time.Duration;
class BeepThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Beep~" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadCreate {
public static void main(String[] args) {
new BeepThread().start();
}
}
Beep~0
Beep~1
Beep~2
Beep~3
Beep~4
Beep~5
Beep~6
Beep~7
Beep~8
Beep~9
实现Runnable接口实现
实现流程
- 编写一个类
- 实现Runnable
- 重写run方法
- new Thread实例
- 传递自己的类
- 调用start方法
需要注意的是要用
Thread
类包装。
class AlarmThread implements Runnable {
@Override
public void run() {
System.out.println("di~");
}
}
public class ThreadCreate {
public static void main(String[] args) {
// 这里需要注意要用Thread类包装
new Thread(new AlarmThread()).start();
}
}
di~
匿名内部类实现
这里纯粹就是java基础知识,没什么好说的。
public class ThreadCreate {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类创建线程");
}
}).start();
}
}
匿名内部类创建线程
lambda表达式实现
这里是java8的基础知识,前面文章说过,这里不做详细概述.
public class ThreadCreate {
public static void main(String[] args) {
new Thread(() -> {
System.out.println("lambda表达式创建线程");
}).start();
}
}
lambda表达式创建线程
Callable 和 Future实现
Callable 和 Future 和上面几种方式不同的是,可以拿到返回值,底层基于
LockSupport
创建流程
- 编写一个类
- 实现Callable
接口 这里的T代表返回值类型
- 重写call方法
调用流程
- 创建实现callable接口类的实例
- 创建一个FutureTask实例
用来接受返回值
- 创建Thread实例,将futuretask实例作为参数传递进去
- 调用Thread实例start方法启动线程.
- 调用FutureTask实例get方法获取线程执行结果
class SoloThread implements Callable<String> {
// 当前线程需要执行的的代码 返回值
@Override
public String call() {
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "gugua~gugua~";
}
}
public class ThreadCreate {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建callable实例
SoloThread soloThread = new SoloThread();
// 用来接受返回值
FutureTask<String> soloFutureTask = new FutureTask<>(soloThread);
// 传递futuretask实例
new Thread(soloFutureTask).start();
// 通过get方法获取返回值
System.out.println(soloFutureTask.get());
}
}
孤寡~孤寡
gugua~gugua~
使用线程池实现
线程池原理:复用机制
创建方式
- Executors.newXXX
- 调用execute方法来执行
- shutdown来关闭线程池
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> {
System.out.println("我是子线程1");
});
executorService.execute(() -> System.out.println("我是子线程2"));
executorService.shutdown();
}
}
我是子线程1
我是子线程2
spring异步注解创建
创建流程
- 开启spring异步注解
@EnableAsync
- @Async标注到要异步的方法上
开启异步支持