JUC学习笔记——进程与线程( 二 )


我们先来介绍同步与异步的实现方式:

  • 同步就是在一个线程内完全执行所有命令
  • 异步可以在多线程中实现,当一个线程执行复杂操作比较耗时时,另一个线程可以执行其他简单操作
我们再来介绍同步与异步的选择方法:
  • 针对比较繁琐的操作,我们通常会单独创建一个新线程来进行处理,避免阻塞主线程
  • Tomcat里面的异步Servlet也是异步操作,让用户线程处理耗时较长的操作,避免阻塞Tomcat的工作线程
  • UI程序中,开线程进行其他操作,避免UI线程
我们分别给出同步异步的代码展示:
// 同步代码package cn.itcast.n2;import cn.itcast.Constants;import cn.itcast.n2.util.FileReader;import lombok.extern.slf4j.Slf4j;@Slf4j(topic = "c.Sync")public class Sync {public static void main(String[] args) {FileReader.read(Constants.MP4_FULL_PATH);log.debug("do other things ...");}}// 异步代码package cn.itcast.n2;import cn.itcast.Constants;import cn.itcast.n2.util.FileReader;import lombok.extern.slf4j.Slf4j;@Slf4j(topic = "c.Async")public class Async {public static void main(String[] args) {new Thread(() -> FileReader.read(Constants.MP4_FULL_PATH)).start();log.debug("do other things ...");}}同步与异步实际使用我们通常采用异步操作来实现应用的速度提升:
// 例如我们有下面三个操作计算 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,别的线程没法干活
此外多核 cpu 可以并行跑多个线程,但能否提高程序运行效率还是要分情况的:
  • 有些任务,经过精心设计,将任务拆分,并行执行,当然可以提高程序的运行效率 。但不是所有计算任务都能拆分
  • 也不是所有任务都需要拆分,任务的目的如果不同,谈拆分和效率没啥意义
最后就是我们的IO操作部分:
  • 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

经验总结扩展阅读