同步与异步实际使用我们通常采用异步操作来实现应用的速度提升:
// 例如我们有下面三个操作计算 1 花费 10 ms计算 2 花费 11 ms计算 3 花费 9 ms汇总需要 1 ms
如果我们采用主线程的同步操作来实现:
// 如果是串行执行,那么总共花费的时间是 10 + 11 + 9 + 1 = 31ms
但是如果我们采用三个CPU的异步操作来实现:
// 但如果是四核 cpu,各个核心分别使用线程 1 执行计算 1,线程 2 执行计算 2,线程 3 执行计算 3// 那么 3 个线程是并行的,花费时间只取决于最长的那个线程运行的时间,即 11ms 最后加上汇总时间只会花费 12ms
下面我们给出同步异步的实际使用规则:
- 多核多线程速度快于单核多线程(异步进行速度较快)
- 单核多线程速度慢于单核单线程(线程切换也需要耗费时间)
- 单核 cpu 下,多线程不能实际提高程序运行效率,只是为了能够在不同的任务之间切换
- 不同线程轮流使用cpu ,不至于一个线程总占用 cpu,别的线程没法干活
- 有些任务,经过精心设计,将任务拆分 , 并行执行 , 当然可以提高程序的运行效率 。但不是所有计算任务都能拆分
- 也不是所有任务都需要拆分,任务的目的如果不同,谈拆分和效率没啥意义
- IO 操作不占用 cpu,只是我们一般拷贝文件使用的是【阻塞 IO】
- 这时相当于线程虽然不用 cpu,但需要一直等待 IO 结束,没能充分利用线程
- 所以才有后面的【非阻塞 IO】和【异步 IO】优化
创建和运行线程我们下面将介绍三种创建和运行线程的方法
直接使用 Thread我们可以直接使用Thread来创建和运行线程:
// 创建线程对象Thread t = new Thread() {public void run() {// 要执行的任务}};// 启动线程t.start();
我们再给出一个实际例子:// 构造方法的参数是给线程指定名字,推荐Thread t1 = new Thread("t1") {@Override// run 方法内实现了要执行的任务public void run() {log.debug("hello");}};t1.start();
我们给出实际输出结果:// 我们会注意到:前面标记了[t1]线程~19:19:00 [t1] c.ThreadStarter - hello
使用 Runnable 配合 Thread这里我们将Thread里面的方法采用Runnable类型的方法来代替:// 创建Runnable类型的方法Runnable runnable = new Runnable() {public void run(){// 要执行的任务}};// 创建线程对象Thread t = new Thread( runnable );// 启动线程t.start();
我们给出一个实际例子:// 创建任务对象Runnable task2 = new Runnable() {@Overridepublic void run() {log.debug("hello");}};// 参数1 是任务对象; 参数2 是线程名字,推荐Thread t2 = new Thread(task2, "t2");t2.start();
其结果为:// 结果正常9:19:00 [t2] c.ThreadStarter - hello
除此之外,我们在JDK8之后,我们可以采用函数式接口Lambda来简化Runnable的书写:// 创建任务对象Runnable task2 = () -> log.debug("hello");// 参数1 是任务对象; 参数2 是线程名字,推荐Thread t2 = new Thread(task2, "t2");t2.start();
【JUC学习笔记——进程与线程】甚至我们都不用定义task,来直接采用Lambda方法书写Thread中的task:// 参数1 是任务对象; 参数2 是线程名字,推荐Thread t2 = new Thread(() -> log.debug("hello"), "t2");t2.start();
底层简单解释:- 至于Thread为什么能够直接调用Runnable
- Thread在接收Runnable类型后,会将其赋值在this.target
- 而Thread的run方法会先来判断是否存在target,如果存在就直接采用target方法
- 方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了
- 用 Runnable 更容易与线程池等高级API 配合
- 用 Runnable 让任务类脱离了 Thread 继承体系 , 更灵活
// 创建任务对象(Integer是返回对象)FutureTask<Integer> task3 = new FutureTask<>(() -> { log.debug("hello"); return 100;});// 参数1 是任务对象; 参数2 是线程名字,推荐new Thread(task3, "t3").start();// 主线程阻塞,同步等待 task 执行完毕的结果Integer result = task3.get();log.debug("结果是:{}", result);
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Seata 1.5.2 源码学习
- 第4版 高性能MySQL 第一章 MySQL架构 读书笔记
- Helm干货!速度围观!
- .NET 源码学习 [数据结构-线性表1.2] 链表与 LinkedList<T>
- MPC:百万富翁问题
- PGL Paddle Graph Learning 关于图计算&图学习的基础知识概览:前置知识点学习
- JVM学习笔记——内存模型篇
- Linux学习环境搭建流程
- Archlinux + Dwm 配置流程
- 关于入门深度学习mnist数据集前向计算的记录