菜虚鲲教你学多线程

背景

不知道菜虚鲲是什么?也许你可以在休闲时间里看看这个视频。你只需要知道,菜虚鲲和视频里的小哥很像,都喜欢唱,跳, rap 和 篮球。这个舞蹈的名称叫做《鸡你太美》,其中的篮球技巧非常的抓人眼球,后面的战术抖肩带也令人回味无穷。好了,背景知识介绍完毕。(以下内容相当不严谨,只为帮助大家理解多线程)

线程与进程

进程就像一个简化版的B站,里面有很多素材(线程)上演各种各样的沙雕视频。观众姥爷们(用户)看着这些沙雕视频很开心,有钱的赏钱,没钱的投个硬币,B站也算是圆满完成了自己的目标,二次元圈其乐融融。当然啦,一个菜虚鲲也是一个线程,它也在为着B站与观众老爷们的欢声笑语献出自己的一份力。

线程的几种状态

一个蔡徐坤被Up主创造(NEW)出来后,点击上传视频按钮(线程的**start()**方法),它就处于一种可以跳鸡你太美的状态了(RUNNABLE)。
现在只要等B站运维小姐姐(CPU调度系统)把它推荐到首页(CPU资源),观众姥爷就可以开看(pen)了。

同学们,我编不下去了。。。。我要开启奔放模式了。

正常来讲,在单线程的情况下,这只菜虚鲲就一直跳鸡你太美(RUNNING),直到跳完(run()方法执行完毕),然后就处于谢幕状态(TERMINATED)了,观众姥爷们也心满意足。

可现实是残酷的,首页坑位只有一个!其他沙雕视频里的菜虚鲲们(多个线程)也要跳鸡你太美!

这种情况下,第一个菜虚鲲(简称cxk001)就很难受。假如它正跳着呢,运维小姐姐突然把首页让给了了另一个视频里的菜虚鲲(cxk002)。cxk001发现,首页被占了,那就只能在角落里自己玩自己(回到RUNNABLE状态),直到它重回主页。

除了首页,还有一个东西很重要。众所周知,菜虚鲲喜欢篮球,鸡你太美里面最精彩的也莫过于它那精湛的篮球技巧。而篮球(临界资源,排他锁),只有一个。

所以在这段篮球炫技片段中,没有篮球的话,菜虚鲲肯定不干的。所以,即便把首页给菜虚鲲,但没有篮球的话,菜虚鲲会处于一种非常沮丧的状态(blocked状态),只有当其他的鲲把篮球让出来给这只鲲,它才能重拾信心,回到RUNNABLE状态,为观众姥爷呈现一段完美的 鸡你太美!。
有些菜虚鲲很有奉献(摸鱼)精神。他说:我先摸会鱼,你们先跳。然后主动把首页和篮球(不都会)让出来一段时间(调用Thread.sleep(long timeout)、 Object.wait(long timeout)。这两者的区别在于,前者是对线程调用,后者对锁调用。前者不会释放锁,后者会) 。还有些鲲很给领导面子,他说:领导先跳,领导跳完了我再跳(调用leaderThread.join(long)), leaderThread是某一个优先级比较高的线程,该方法同样也会释放锁)。这些主动让出首页的菜虚鲲就处于一种 超时等待状态(TIMED_WAITING)。当摸鱼时间结束后,它就又处于可以跳鸡你太美的状态了(RUNNABLE)。

还有一种情况,那就是多管闲事菜虚鲲,动不动给其他鲲发律师函, 让那些没拿到篮球的菜虚鲲起来跳鸡你太美。有的只随机给一个鲲发(调用锁的notify()方法),有的比较绝,给所有人都发(调用锁的notifyAll()方法),被发函的鲲就得回到RUNNABLE状态,心里美滋滋。

有的鲲心累啊,想跳不给跳,首页又不给,心态爆炸,直接说,老子不要球了,不跳了(调用锁对象的wait()方法,注意该方法没有指定时间长短)。此时,这只鲲就进入到无限期等待状态(WAITING)状态,这时候就只能等那些多管闲事的鲲来给自己发律师函,才能回到RUNNABLE状态。

现在,一只菜虚鲲的生命历程我们基本已经了解大半了。但是我们漏了一个重要情报!!!
怎么让篮球呢,当然是靠那些喜欢发律师函的鲲了(具体来讲,是通过调用篮球的notify()和notifyAll()方法)。

至此,我们也就介绍清楚了线程的整个生命周期。

下面开始一个菜虚鲲的一生总结:

  1. 被Up主创建出来(NEW
  2. Up主点击上传按钮,菜虚鲲准备好了,但还没有获得篮球或者首页坑位(RUNNABLE
  3. 获得篮球以及首页坑位,表演 鸡你太美!(RUNNING
  4. 失去篮球,对人生失去信心(BLOCKED
  5. 重新获得篮球,重拾信心(RUNNABLE
  6. 表演完毕,完美谢幕(TERMINATED
  7. 角落里摸鱼,好不自在(TIMED_WAITING,WAITING

下面开始一个菜虚鲲的关键行为总结:

  1. 进行摸鱼(Object.wait (), Thread.sleep())。这两个方法都会放弃首页(cpu资源),前者会放弃锁,后者不会。
  2. 给 一条/所有 菜虚鲲发律师函(lockObject.notify(), lockObject.notifyAll())。该行为本质是唤醒那些需要锁资源的线程,lockObject也就是当前线程所持有的锁对象。
  3. 把首页让给其他领导菜虚鲲(leaderThread.join(), leaderThread是程序设计中的其他高优先级线程),让出首页和篮球的鲲处于阻塞状态。需要注意的点是,如果鲲1让鲲2 join,但是此时鲲2还没有被创建的话,鲲1还是要继续跳下去的。

举个栗子

package org.oe.notify;

import java.util.ArrayList;
import java.util.List;

/**
 * @program: learning-multi-thread
 * @description: 菜虚鲲教你多线程
 * @author: oe
 * @create: 2019-04-29 14:15
 */
public class XuKunCai implements Runnable{

  private Basketball basketball;
  public static final String PERCENTAGE_PLACEHOLDER = "%";
  public static final String RUNNING_REMARK = "正在表演 鸡你太美,当前进度:";
  public static final String WAIT_REMARK = "让出了球权,下面有请他的弟弟们上台表演:";
  public static final String NOTIFIED_REMARK = "弟弟被唤醒了,接下来看我的:";
  private int progress;

  public XuKunCai(Basketball basketball, int progress) {
    this.basketball = basketball;
    this.progress = progress;
  }
  public void run() {
    dancingBeautifulChicken(basketball, 0);
  }
  public void dancingBeautifulChicken(Basketball basketball, int schedule) {

    synchronized (basketball) {
      while ((schedule = schedule + 50) <= 100) {
        try {
          basketball.notify();
          System.out.println(Thread.currentThread().getName() + RUNNING_REMARK + schedule + PERCENTAGE_PLACEHOLDER);
          System.out.println(Thread.currentThread().getName() + WAIT_REMARK);
          basketball.wait(1000);
          System.out.println(Thread.currentThread().getName() + NOTIFIED_REMARK);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.println(Thread.currentThread().getName() + "表演完毕");
      basketball.notify();

    }

  }


  public static void main(String[] args) {
    Basketball basketball = new Basketball();
    List<Thread> threadList = new ArrayList<>();
    for (int i = 0; i< 3; i++) {
      threadList.add(new Thread(new XuKunCai(basketball, 0), "菜虚鲲00"+(i + 1)));
    }
    threadList.get(0).setPriority(10);
    threadList.get(1).setPriority(1);
    threadList.get(2).setPriority(1);
    threadList.forEach(thread -> thread.start());

    boolean finish = false;
    while (!finish) {
      for (Thread thread : threadList) {
        if (thread.getState() == Thread.State.BLOCKED) {
          System.out.println(Thread.currentThread().getName() + ":我是"+ thread.getName() +
                  ",优先级是"+thread.getPriority()+",我要报警了" +"又没拿到球");
          try {
            thread.join();
          }catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
      if (threadList.get(0).getState() == Thread.State.TERMINATED &&
              threadList.get(1).getState() == Thread.State.TERMINATED &&
              threadList.get(0).getState() == Thread.State.TERMINATED ) {
        finish = true;
      }
    }
  }


  private static class Basketball {

  }
}

在这一实例中,我创建了三个菜虚鲲实例(这里采用的是实现Runnable接口),它们在Run()方法中执行的任务是dancingBeautifulChicken()方法,该方法将打印该鲲的跳舞进度,每跳50%,就让出临界资源:basketball对象,并唤醒其他阻塞的线程。最终,三只鲲都跳完鸡你太美,返回main()主线程。值得注意的是,由于dancingBeautifulChicken()方法中加锁的对象是basketball,所以释放锁和唤醒其他线程的操作也都是对basketball调用相应方法完成的。

执行结果如下,这里我有个疑问,我对三个线程设置的优先级没有起到作用,有知道原因的小伙伴请帮我解答一下。

菜虚鲲001正在表演 鸡你太美,当前进度:50%
main:我是菜虚鲲002,优先级是1,我要报警了又没拿到球
菜虚鲲001让出了球权,下面有请他的弟弟们上台表演:
菜虚鲲003正在表演 鸡你太美,当前进度:50%
菜虚鲲003让出了球权,下面有请他的弟弟们上台表演:
菜虚鲲002正在表演 鸡你太美,当前进度:50%
菜虚鲲002让出了球权,下面有请他的弟弟们上台表演:
菜虚鲲003弟弟被唤醒了,接下来看我的:
菜虚鲲003正在表演 鸡你太美,当前进度:100%
菜虚鲲003让出了球权,下面有请他的弟弟们上台表演:
菜虚鲲002弟弟被唤醒了,接下来看我的:
菜虚鲲002正在表演 鸡你太美,当前进度:100%
菜虚鲲002让出了球权,下面有请他的弟弟们上台表演:
菜虚鲲003弟弟被唤醒了,接下来看我的:
菜虚鲲003表演完毕
菜虚鲲002弟弟被唤醒了,接下来看我的:
菜虚鲲002表演完毕
菜虚鲲001弟弟被唤醒了,接下来看我的:
菜虚鲲001正在表演 鸡你太美,当前进度:100%
菜虚鲲001让出了球权,下面有请他的弟弟们上台表演:
菜虚鲲001弟弟被唤醒了,接下来看我的:
菜虚鲲001表演完毕

Process finished with exit code 0

总结

本篇文章逻辑很不严谨,不太像博客,甚至有点像吐槽,大家看看笑一笑也就行了。目的在于给学习java多线程知识的你提供一种思路,欢迎评论区讨论。

声明:本文中出现的人物与事件均与现实世界无关,如有雷同,纯属偶合,拒绝接受任何律师函。
本文作者保有对该文章的最终解释权。

最后,转载本文请标明作者和出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值