上次小白一起参观完王总鞋厂后,回去就赶紧把代码工程化搞好了。
同时也大受震撼,王总这个鞋厂月销百万,那算下来每天得有一万双鞋子吧?(小白的数学怕不是体育老师教的!!!)
还有这么大的量,它是怎么做到的啊?这个思想能用在编程里面吗?
哇,小白问出这个问题很有创造性!
哇,雷学委认为多线程这一块很重要啊,很多小白写很多代码,总停留在CRUD或写方法倒腾数据,根本提升不了!
这里学委准备了一个短视频:
《雷学委趣味编程故事汇编》系列之多线程基础
下面学委继续鞋厂流水线进行讲解。
来,我们一起又看看,大型鞋厂生产线
下图是生产线一个打包环节的工人们,可爱的妹子们在认真的包装鞋子制品(多么灿烂的笑容啊!)
图片来自,中国皮革人才网@东莞鞋厂将在贵州建12条生产线,解决2400余人就业
小白,你看这个车间是不是很多人,很多鞋子。
如果生产一万双鞋子,一百个员工,一人分配一百双鞋子,八小时工作制,那么每个小时不就是12.5双鞋子,还可以吧
10000 / 100 / 8 = 12.5
不过月销百万双鞋子,按照22天工作,那么每个小时处理的鞋子数量得达到56.8,也就是一分钟一双鞋!
1000000/ 22 / 100 / 8 = 56.8
不过,实际情况,很可能工厂同样的量聘请的工人没有这里说的100那么多,但处理的量和工作时长更长,挺辛苦的!
可以看看下面的视频,一直都挺忙碌!
图片截图自视频:实拍鞋厂里的生产流水线,原来是这样制作的,真是让我开眼界了!
小白此刻并没有很高兴,看着有点心疼工人朋友们。但他是清醒的,他认为得继续学习,抱怨没用的不如提升自己,以后发挥更多正向作用!
他继续问:那这些生活流水线,交给程序自动化不就好了吗,程序怎么做的?我想学!
好的,那请耐心认真学习。
什么是线程(Thread),不是现成!
直观来理解,每一个劳碌工作的工人就像一个线程,然后3.3万双鞋子就是每天的工作量。连续运行21天,就能生产接近100万双鞋子。(后面会把这个代码实现了!小白务必收藏多运行,掌握,这是成为中高级程序员必须迈过的坎)
线程可以理解成是在Java进程中独立运行的子任务。
进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。
它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
下面是Java中一个线程的实现:
package org.levintech.javademo.leixuewei.multithread; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; /** * 一个简单打工人线程 * * @author levin * Created on 2021/7/10 * [雷学委] CSDN/Juejin Code Demo */ public class SimpleWorker extends Thread { private String workerName; private Integer batchSize; public SimpleWorker(String workerName, Integer batchSize) { this.workerName = workerName; this.batchSize = batchSize; } @Override public String toString() { return "SimpleWorker{" + "workerName='" + workerName + ",batchSize='" + batchSize + '}'; } @Override//重写run方法,进行线程业务处理 public void run() { System.out.println("【雷学委代码Demo】工人" + toString()); } public String getWorkerName() { return workerName; } public Integer getBatchSize() { return batchSize; } }
定义线程有好几种方式,本文只重点展示一种!其他懂的可以评论区留言,验证一下你的知识,哈哈。
运行项目中的LeiXueWeiRunner,可以查看如下运行效果:
小白说我这里看有一个state,是状态吗?
没错,线程跟工人一样也是有状态,也会朝气蓬勃,也会精疲力竭的。
线程的状态总共6中的,具体如下图:
小白说:那是不是new创建Thread或者继承类,就是新建状态(NEW);接着调用了start之后线程就是运行状态(RUNNABLE),然后没别的事情就顺利结束(TERMINATED)?
雷学委:是的,没错。至于线程等待别的线程(比如调用了join方法)就转变为等待状态(WAITING或TIMED_WAITING)。如果线程执行过程需要获取一个锁定的资源,那么就是阻塞状态(BLOCKED)
小白好像对阻塞状态不太懂?
打个比方,假设你家里只有一串钥匙,几个柜子都用这串钥匙来开,但此时你妹妹在用它来打开A柜子,那你是不是没发去打开B柜子了?只能等她开完把钥匙给你开锁。如果她拿完东西,跑出去玩了,哈哈哈,那结果就你这线程一直等待!!!
嗯,明白了。(小白用力的点头)
官方给的线程状态介绍在这里: https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.State.html
不错,你已经掌握了多线程基础,已经学会如何创建线程知道线程的状态,到这里可以三连了!
下面直接看百万订单是如何被处理的?
下面学委写了一个新的类(继承了普通工人),其实吧,“高级”打工人也还是打工人,大家都是一样的,应该共同致力搞好工作氛围!
小白你准备好了吗?雷学委要直接上最难代码了。(小白估计只能懂个故事了,代码他估计下次还会来问!)
小白信心满满,上面的那么简单,接下来估计不会难到哪去吧。
下面是高级打工人的代码实现,在看代码后面解析之前,请自己想想为什么要这样写?
/** * 一个"高级"打工人线程 * * @author levin * Created on 2021/7/10 * [雷学委] CSDN/Juejin Code Demo */ public class Worker extends SimpleWorker { private LinkedBlockingQueue<Integer> workItems; private ConcurrentHashMap<String, List<Integer>> status; public Worker(String workerName, Integer batchSize, LinkedBlockingQueue<Integer> workItems, ConcurrentHashMap<String, List<Integer>> status) { super(workerName, batchSize); this.workItems = workItems; this.status = status; } @Override public String toString() { return "Worker{" + "workerName='" + super.getWorkerName() + ",batchSize='" + super.getBatchSize() + ",thread=" + super.getName() + ",threadId=" + super.getId() + ",threadPriority=" + super.getPriority() + ",threadState=" + super.getState() + ",threadGroup=" + super.getThreadGroup() + ",threadUncaughtExceptionHandler=" + super.getUncaughtExceptionHandler() + '}'; } @Override public void run() { System.out.println("【雷学委代码Demo】工人" + toString()); int handledNum = 0; List<Integer> items = new ArrayList<>(); while (handledNum < this.getBatchSize() && !workItems.isEmpty()) { int shoeId = workItems.poll(); System.out.println("【雷学委代码Demo】" + this.getWorkerName() + " 包装了鞋子,序号=" + shoeId); handledNum++; items.add(shoeId); } status.put(this.getWorkerName(), items); } }
下面是调用代码:
package org.levintech.javademo.leixuewei.multithread; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import static org.levintech.javademo.leixuewei.multithread.LeiXueWeiRunner01.doAndPrintWorkItem; import static org.levintech.javademo.leixuewei.multithread.LeiXueWeiRunner01.generateWorkItems; /** * @author levin * Created on 2021/6/19 * [雷学委] CSDN/Juejin Code Demo */ public class LeiXueWeiRunner02 { public static void main(String[] args) throws InterruptedException { System.out.println("【雷学委代码Demo】"); final int totalCount = 1000000; LinkedBlockingQueue<Integer> workItems = generateWorkItems(totalCount);//生成100万双鞋子 ConcurrentHashMap<String, List<Integer>> status = new ConcurrentHashMap<>(); //doAndPrintWorkItem(workItems); for (int i = 0; i < 100; i++) { Worker leiXiaoHua = new Worker("雷小花" + (i + 1), 22 * 8 * 57, workItems, status); leiXiaoHua.start(); } while (!workItems.isEmpty()) { System.out.println("工人在忙工作,不要催,请耐心等待!....剩余:" + workItems.size()); Thread.sleep(3 * 1000); } int actualTotalCount = 0; for (Map.Entry<String, List<Integer>> entry : status.entrySet()) { actualTotalCount += entry.getValue().size(); } System.out.println("totalCount=" + actualTotalCount); } }
此时小白一脸懵了!
代码解析
这里学委创建了一个线程安全的队列 LinkedBlockingQueue 和一个记录工人工作情况的一个ConcurrentHashMap。
然后创建100个工人(带上工号,每个工人最大处理包装的数量,对接到鞋子队列,对接到反馈工作状态的map),他们几乎同时的处理队列上的鞋子,完成包装
在Woker类里面,每个员工都会检查是否这个月的工作量达标了handledNum,如果不达标继续包装,可以去休息了。或者流水线上面没有鞋子了,那也可以去休息了。休息之前填一下表,记录一下自己做了多少业绩。
最后在主程序LeiXueWeiRunner02里面,这里其实跟其他100个worker是同时运行的,只是它一直等待所有鞋子处理好(队列清空的情况),然后检查每个工人业绩,统计是否漏单了。
程序最后核对结果为100万,跟最初生成的100万匹配!(这里很值得三连一下的,这不是一个容易的程序)
小白说:懂了道理了,代码还得多看,不太熟啊(我看他压根就没用过吧。)
整个过程也就跟下图一致,还是建议你三连后多看看视频:
敲敲黑板,读者朋友能不能做到一样呢?
这个你得多练!学委只对文章负责,而你需要对自己负责!就像我们看小破站那些教你写字的,别人一会功夫写了一手好字,你写不出来怎么办?多练多写呗,仅此!