趣味故事掌握多线程基础 - 揭秘大型鞋厂百万并发产能内幕【视频+多图建议收藏】

简介: 雷学委趣讲编程 - 揭秘大型鞋厂百万产能内幕

上次小白一起参观完王总鞋厂后,回去就赶紧把代码工程化搞好了。


同时也大受震撼,王总这个鞋厂月销百万,那算下来每天得有一万双鞋子吧?(小白的数学怕不是体育老师教的!!!)


还有这么大的量,它是怎么做到的啊?这个思想能用在编程里面吗?


哇,小白问出这个问题很有创造性!


哇,雷学委认为多线程这一块很重要啊,很多小白写很多代码,总停留在CRUD或写方法倒腾数据,根本提升不了!


这里学委准备了一个短视频:



image.png

《雷学委趣味编程故事汇编》系列之多线程基础

下面学委继续鞋厂流水线进行讲解。

image.png

来,我们一起又看看,大型鞋厂生产线

下图是生产线一个打包环节的工人们,可爱的妹子们在认真的包装鞋子制品(多么灿烂的笑容啊!)

image.png

图片来自,中国皮革人才网@东莞鞋厂将在贵州建12条生产线,解决2400余人就业

小白,你看这个车间是不是很多人,很多鞋子。

如果生产一万双鞋子,一百个员工,一人分配一百双鞋子,八小时工作制,那么每个小时不就是12.5双鞋子,还可以吧

10000 / 100 / 8 = 12.5

不过月销百万双鞋子,按照22天工作,那么每个小时处理的鞋子数量得达到56.8,也就是一分钟一双鞋!

1000000/ 22 / 100 / 8 = 56.8

不过,实际情况,很可能工厂同样的量聘请的工人没有这里说的100那么多,但处理的量和工作时长更长,挺辛苦的!

可以看看下面的视频,一直都挺忙碌!

image.png

图片截图自视频:实拍鞋厂里的生产流水线,原来是这样制作的,真是让我开眼界了!


小白此刻并没有很高兴,看着有点心疼工人朋友们。但他是清醒的,他认为得继续学习,抱怨没用的不如提升自己,以后发挥更多正向作用!


他继续问:那这些生活流水线,交给程序自动化不就好了吗,程序怎么做的?我想学!


好的,那请耐心认真学习。


什么是线程(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;
    }
}

定义线程有好几种方式,本文只重点展示一种!其他懂的可以评论区留言,验证一下你的知识,哈哈。

image.png

运行项目中的LeiXueWeiRunner,可以查看如下运行效果:

image.png

小白说我这里看有一个state,是状态吗?

没错,线程跟工人一样也是有状态,也会朝气蓬勃,也会精疲力竭的。

线程的状态总共6中的,具体如下图:

image.png

小白说:那是不是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万匹配!(这里很值得三连一下的,这不是一个容易的程序)


小白说:懂了道理了,代码还得多看,不太熟啊(我看他压根就没用过吧。)


整个过程也就跟下图一致,还是建议你三连后多看看视频:


image.png

敲敲黑板,读者朋友能不能做到一样呢?

这个你得多练!学委只对文章负责,而你需要对自己负责!就像我们看小破站那些教你写字的,别人一会功夫写了一手好字,你写不出来怎么办?多练多写呗,仅此!

目录
相关文章
|
28天前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
131 0
|
13天前
|
安全
List并发线程安全问题
【10月更文挑战第21天】`List` 并发线程安全问题是多线程编程中一个非常重要的问题,需要我们认真对待和处理。只有通过不断地学习和实践,我们才能更好地掌握多线程编程的技巧和方法,提高程序的性能和稳定性。
124 59
|
4天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
3月前
|
Java 开发者
解锁并发编程新姿势!深度揭秘AQS独占锁&ReentrantLock重入锁奥秘,Condition条件变量让你玩转线程协作,秒变并发大神!
【8月更文挑战第4天】AQS是Java并发编程的核心框架,为锁和同步器提供基础结构。ReentrantLock基于AQS实现可重入互斥锁,比`synchronized`更灵活,支持可中断锁获取及超时控制。通过维护计数器实现锁的重入性。Condition接口允许ReentrantLock创建多个条件变量,支持细粒度线程协作,超越了传统`wait`/`notify`机制,助力开发者构建高效可靠的并发应用。
89 0
|
26天前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
25 1
|
28天前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
142 0
|
2月前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
3月前
|
算法 Java
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
该博客文章综合介绍了Java并发编程的基础知识,包括线程与进程的区别、并发与并行的概念、线程的生命周期状态、`sleep`与`wait`方法的差异、`Lock`接口及其实现类与`synchronized`关键字的对比,以及生产者和消费者问题的解决方案和使用`Condition`对象替代`synchronized`关键字的方法。
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
|
2月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
56 0
|
2月前
|
C语言
C语言 网络编程(九)并发的UDP服务端 以线程完成功能
这是一个基于UDP协议的客户端和服务端程序,其中服务端采用多线程并发处理客户端请求。客户端通过UDP向服务端发送登录请求,并根据登录结果与服务端的新子线程进行后续交互。服务端在主线程中接收客户端请求并创建新线程处理登录验证及后续通信,子线程创建新的套接字并与客户端进行数据交换。该程序展示了如何利用线程和UDP实现简单的并发服务器架构。