并发编程实战1-多线程的周期、实现方式和意义

day01:线程的周期

线程的周期

day02:创建线程的多种方式(7中):
一般推荐采用Runnable接口或者Callable接口来实现多线程。

西红丝鸡蛋汤:

  1. 继承Thread类
    • 主线成为最简单的汤的做法,现在需要仿照这个做法,定做自己的
    • 直接继承,可以使用一些父线程的共同属性
  2. 实现Runnable接口
    • 做汤的方法都知道,但是需要自己安排
    • 还可以继承其他类,可拓展性比较好
    • 由于接口内部调用的是同一个target,多个线程共享一个target,所以更适合多线程共享统一资源。
      1
      2
      3
      4
      5
      6
      @Override
      public void run() {
      if (target != null) {
      target.run();
      }
      }
  3. 带返回值的线程/实现Callable接口的
    • 大体的方式都知道,但是需要缺少西红柿,需要开个线程,把西红柿提前做好用于备用(返回值),等汤都做好了,只需要向其中加入西红柿就可以了(调用)
    • 同第二种方式类似,只是这里的使用了FutureTask,作用相当于提前分配一个线程任务用于计算一个程序的结果,这个结果将在之后的主线程调用int result = futureTask.get();

  1. 匿名内部类的方式
    • 我的主要目的是做一顿晚饭(主类),所以西红柿鸡蛋汤并不是主体(只是主体的一部分),这时候,只需要在做饭内部,定义一个做西红柿鸡蛋汤的匿名内部类就行了。
    • 匿名内部类,相当于直接在主线程中加入一个内部类,相当于一个小的脚本,不影响主类。
  2. 定时器(quartz)
    • 为了准确把握做汤的时间,我们需要一个定时器,仅仅是sleep不能满足要求。因为,有时候,这个定时器需求蛮复杂,有些时候,我们做汤,需要隔一分钟尝一次味道,所以,这个时候就需要定时器,固定时间重复一个线程。
  3. 线程池的实现
    • 由于汤做的太好了,开启了饭店,由于顾客很多,为了满足需求,需要很多的做汤程序,为了方便,这个时候就需要雇佣一批厨师(线程池),顾客随时到来,随时就可以启动做汤,但是,由于顾客流量不定,厨师太多太少,都不好。通过分析客户流量,发现10个厨师,刚好满足要求,所以就设置线程池大小为10。
    • 为了满足高并发要求,需要提前备用多个线程在进行操作。
  4. Lambda表达式实现
    • 同上,需求很大时对西红柿的要求也很大,所以洗西红柿也需要多个人来进行,而且,和做汤不同,洗西红柿的只会洗西红柿(数据计算),所以jdk8就启用了一个lambda表达式,让这些专职工人只做一件事。
    • lambda表达式有一个数据计算并行流parallelStream,可以对list等直接进行并行计算:return list.parallelStream().mapToInt(i -> i * 2).sum();
  5. Spring实现多线程
    • spring中有个@Async注解,可以实现多线程

day03:多线程几个主要指标

  • 一、活跃性问题

    • 死锁:即线程资源调用互斥,哲学家吃饭问题(刀叉互斥)

    • 饥饿(不公平):

      • 由于某个线程太弱,优先级太低的问题,导致这个线程始终无法抢到CPU资源,就会导致,这个线程饥饿(排队买饭,前面总是有插队的,你就饿死了)
      • 某一个线程获取了资源的锁,一直占用着,导致另一个线程无法获取此资源,也会饥饿
      • wait和notify,线程wait后,没有其他线程来notify它,也会被饿死。
    • 活锁:之所以称之为活锁,是相对与死锁而言,活锁并没有阻塞,还是在正常运行,只是,这种运行,执行一部分就回滚,一直在重复,相当于一直在做无用功。一般出现在事务回滚中,由于两个线程资源故障,导致都一起回滚,然而,重新开始,还是遇到了这个问题,就一起一直回滚下去。比如,两台机器发送数据,由于通道同时被占用,就都等了一秒,再发,这样冲突还是存在。

    • 如何避免饥饿?

      • 设置合理的优先级
      • 使用自己实现的锁来代替synchronized,自己会合理的设置锁
  • 二、性能问题:多线程并不一定快

    • 线程多了,CPU不够,导致一个CPU要应付很多线程,由于线程之间切换,需要保存数据和提取数据,导致切换回占用太多的时间,就会降低整体速度(上下文切换)
  • 三、安全性问题

    • 出现安全性问题的三个条件:
      • 多线程环境
      • 共享同一个资源
      • 对资源进行非原子性操作

多线程的目的是为了将可以并发操作的操作进行并发执行,而将需要原子性操作的资源进行保护,所以之后会使用到细粒度锁。

源码git

Donate comment here