Java_多线程那点事

数字孪生变电站如何制作?虚拟现实项目强荐广州华锐互动 变电站数字孪生系统可以为提高变电站设备与环境的实时感知能力、在线诊断设备健康状态、促进设备隐藏故障定位和维修效率的提高、实现设备全生命周期提供有力支撑。通过对设备状态的准确评估,可以延长设备的生命周期,实现资产增值。1、3D可视化:建立可视化平台,集中展示所有3D模型信息、数据信息、环境信息和人员信息,包括设备数字模型、建筑外形及内部、周边地形、人员动态、天气、时间等。4、智能监控:采用先进的AI摄像头,通过设备的图像识别,自动智能分析图像,准确分析设备状态、运行动态、外围控制情况。... 阅读详情

Java_多线程那点事

在这里插入图片描述

Java内多线程函数含义
synchronized
  • Java中每一个对象都可以成为一个监视器(Monitor), 该Monitor由一个锁(lock), 一个等待队列(waiting queue) , 一个入口队列(entry queue)组成。
  • 对于一个对象的方法, 如果没有synchronized关键字修饰, 该方法可以被任意数量的线程,在任意时刻调用。
    对于添加了synchronized关键字的方法,任意时刻只能被唯一的一个获得了对象实例锁的线程调用。
  • synchronized用于实现多线程的同步操作
Wait
  • wait(), notify(), notifyAll()synchronized 需要搭配使用, 用于线程同步
  • wait()总是在一个循环中被调用,挂起当前线程来等待一个条件的成立。 Wait调用会一直等到其他线程调用notifyAll()时才返回。
  • wait() 在其他条件不满足时,将获得的锁释放掉
  • 当一个线程在执行synchronized的方法内部,调用了wait()后, 该线程会释放该对象的锁, 然后该线程会被添加到该对象的等待队列中(waiting queue), 只要该线程在等待队列中, 就会一直处于阻塞状态,不会被调度执行。 要注意wait()方法会强迫线程先进行释放锁操作,所以在调用wait()时, 该线程必须已经获得锁,否则会抛出异常。但是由于wait()synchronized的方法内部被执行, 锁一定已经获得, 就不会抛出异常了。
Notify
  • 当一个线程调用一个对象的notify()方法时, 调度器会从所有处于该对象等待队列(waiting queue)的线程中取出任意一个线程, 将其添加到入口队列(entry queue)中. 然后在入口队列中的多个线程就会竞争对象的锁, 得到锁的线程就可以继续执行。 如果等待队列中(waiting queue)没有线程, notify()方法不会产生任何作用
  • notifyAll()notify()工作机制一样, 区别在于notifyAll()会将等待队列(waiting queue)中所有的线程都添加到入口队列中(entry queue)
  • notifyAll()notify()更加常用, 因为notify()方法只会唤起一个线程,且无法指定唤醒哪一个线程

多线程的几种状态
  • 新建

    • 当一个线程对象被创建,但还未调用 start 方法时处于新建状态
    • 此时还只是Java对象,还未与操作系统底层线程关联
  • 可运行

    • 调用了 start 方法,就会由新建进入可运行
    • 此时与底层线程关联,由操作系统调度执行
  • 终结

    • 线程内代码已经执行完毕,由可运行进入终结
    • 此时会取消与底层线程关联
  • 阻塞

    • 获取锁失败后,由可运行进入 Monitor 的阻塞队列阻塞,此时不占用 cpu 时间
    • 当持锁线程释放锁时,会按照一定规则唤醒阻塞队列中的阻塞线程,唤醒后的线程进入可运行状态
  • 等待

    • 当获取锁成功后,但由于条件不满足,调用了 wait()方法,此时从可运行状态释放锁进入 Monitor 等待集合等待,同样不占用 cpu 时间
    • 当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的等待线程,恢复为可运行状态
  • 有时限等待

    • 当获取锁成功后,但由于条件不满足,调用了 wait(long) 方法,此时从可运行状态释放锁进入 Monitor 等待集合进行有时限等待,同样不占用 cpu 时间
    • 当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的有时限等待线程,恢复为可运行状态,并重新去竞争锁
    • 如果等待超时,也会从有时限等待状态恢复为可运行状态,并重新去竞争锁
    • 还有一种情况是调用 sleep(long) 方法也会从可运行状态进入有时限等待状态,但与 Monitor 无关,不需要主动唤醒,超时时间到自然恢复为可运行状态

    注意:

    • 操作系统层面将阻塞 等待 有限时等待都作为阻塞

    • 一个线程调用了wait()之后, 必然需要由另外一个线程调用notify()来唤醒该线程


锁的种类
  • 独占锁:所谓独占锁,就是某一时刻,该锁只能被一个线程占有的

  • 共享锁:共享锁则是某一时刻,该锁可以被多个线程同时占有

  • 公平锁:获取锁的顺序,和请求锁的顺序是一致的

  • 非公平锁:获取锁的顺序,和请求锁的顺序不一致,存在抢占机制


线程池

在这里插入图片描述

wait vs sleep

共同点

  • wait() ,wait(long) 和 sleep(long) 的效果都是让当前线程暂时放弃 CPU 的使用权,进入阻塞状态

不同点

  • 方法归属不同
    • sleep(long) 是 Thread 的静态方法
    • 而 wait(),wait(long) 都是 Object 的成员方法,每个对象都有
  • 醒来时机不同
    • 执行 sleep(long) 和 wait(long) 的线程都会在等待相应毫秒后醒来
    • wait(long) 和 wait() 还可以被 notify 唤醒,wait() 如果不唤醒就一直等下去
    • 它们都可以被打断唤醒
  • 锁特性不同(重点)
    • wait 方法的调用必须先获取 wait 对象的锁,而 sleep 则无此限制
    • wait 方法执行后会释放对象锁,允许其它线程获得该对象锁(我放弃 cpu,但你们还可以用)
    • 而 sleep 如果在 synchronized 代码块中执行,并不会释放对象锁(我放弃 cpu,你们也用不了)
  • 其他
    • wait必须拥有锁才可以使用,而sleep可以不需要锁


lock vs synchronized
  • 语法层面

    • synchronized 是关键字,源码在 jvm中,用 c++ 语言实现
    • Lock 是接口,源码由 jdk 提供,用 java 语言实现
    • 使用 synchronized 时,退出同步代码块锁会自动释放,而使用 Lock 时,需要手动调用 unlock 方法释放锁(使用final进行解锁)
  • 功能层面

    • 二者均属于悲观锁、都具备基本的互斥、同步、锁重入功能
    • Lock 提供了许多 synchronized 不具备的功能,例如获取等待状态、公平锁、可打断、可超时、多条件变量
    • Lock 有适合不同场景的实现,如 ReentrantLock, ReentrantReadWriteLock
  • 性能层面

    • 在没有竞争时,synchronized 做了很多优化,如偏向锁、轻量级锁,性能不赖

    • 在竞争激烈时,Lock 的实现通常会提供更好的性能


公平锁

  • 公平锁的公平体现
    • 已经处在阻塞队列中的线程(不考虑超时)始终都是公平的,先进先出
    • 公平锁是指未处于阻塞队列中的线程来争抢锁,如果队列不为空,则老实到队尾等待
    • 非公平锁是指未处于阻塞队列中的线程来争抢锁,与队列头唤醒的线程去竞争,谁抢到算谁的
  • 公平锁会降低吞吐量,一般不用

条件变量

	持有锁,但是其他条件不满足,先放弃锁,在condition队列中等待
  • ReentrantLock 中的条件变量功能类似于普通 synchronized 的 wait,notify,用在当线程获得锁后,发现条件不满足时,临时等待的链表结构
  • synchronized 的等待集合不同之处在于,ReentrantLock 中的条件变量可以有多个,可以实现更精细的等待、唤醒控制
volatile

线程安全需要考虑三个方面:可见性、有序性、原子性

  • **可见性:**一个线程对共享变量进行修改,另一个线程可以看到最新的结果
  • **有序性:**一个线程内代码按编写顺序执行
  • **原子性:**一个线程内多行代码以一个整体运行,期间不能有其他线程的代码插队

volatile 可以保证可见性与有序性,但是不能保证原子性,因此,volatile 在多线程并发修改某个变量时依然会出现并发问题,并非完全安全的。首先推荐使用锁来解决问题。

  • **保证可见性:**用volatile修饰的共享变量,能够防止编译器等jit优化发生,让一个线程对共享变量的修改对另一个线程可见

    jit 是 just in time 的缩写, 也就是即时编译编译器,用来进行优化程序,使得程序运行效率提高

  • **保证有序性:**用volatile修饰共享变量会在读、写共享变量时加入不同的内存屏障,阻止其他读写操作越过屏障,从而达到阻止重排序的结果

使用内存屏障来解决指令重排序的问题,使得指令按顺序执行。

对于有序性,此处有需要注意的点:

  • volatile变量写加的屏障是阻止上方其它写操作越过屏障排到 volatile 变量写之下

  • volatile 变量读加的屏障是阻止下方其它读操作越过屏障排到 volatile 变量读之上

  • volatile 读写加入的屏障只能防止同一线程内的指令重排


悲观锁 VS 乐观锁
  • 悲观锁的代表是 synchronized 和 Lock 锁

    • 其核心思想是【线程只有占有了锁能去操作共享变量,每次只有一个线程占锁成功,获取锁失败的线程,都得停下来等待
    • 线程从运行到阻塞、再从阻塞到唤醒,涉及线程上下文切换,如果频繁发生,影响性能
    • 实际上,线程在获取 synchronized 和 Lock 锁时,如果锁已被占用,都会做几次重试操作,减少阻塞的机会
  • 乐观锁的代表是 AtomicInteger,使用 CAS 来保证原子性

    • 其核心思想是【无需加锁,每次只有一个线程能成功修改共享变量,其它失败的线程不需要停止,不断重试直至成功】
    • 由于线程一直运行,不需要阻塞,因此不涉及线程上下文切换
    • 它需要多核 cpu 支持,且线程数不应超过 cpu 核数

    【补】CAS:Compare And Swap(比较并交换),底层是有三个操作数,分别是内存值、预期值、更新值。只有当内存值和预期值相等的时候,才会去修改为更新值。

    • 优点:避免死锁的产生,没有锁竞争

    • 缺点:

      • 底层是通过do-while来完成锁自旋,开销较大

      • 会导致ABA问题(所谓的ABA问题就是A线程修改B中的原本值修改为新值,另一个A线程又将新值修改为原本值,B无法得知这一变化)

      • 只能保证单个变量


Hashtable 与 ConcurrentHashMap
  • Hashtable 与 ConcurrentHashMap 都是线程安全的 Map 集合

  • Hashtable 与 ConcurrentHashMap他们都键值都不能为空

  • Hashtable 并发度低,整个 Hashtable 对应一把锁,同一时刻,只能有一个线程操作它

  • ConcurrentHashMap 并发度高,整个 ConcurrentHashMap 对应多把锁,只要线程访问的是不同锁,那么不会冲突


细说ConcurrentHashMap

在JDK1.7中:

  • 数据结构:Segment(大数组) + HashEntry(小数组) + 链表,每个 Segment 对应一把锁,如果多个线程访问不同的 Segment,则不会冲突
  • **并发度:**Segment 数组大小即并发度,决定了同一时刻最多能有多少个线程并发访问。Segment 数组不能扩容,意味着并发度在 ConcurrentHashMap 创建时就固定了
  • 索引计算:
    • 假设大数组长度是 2的m次方,key 在大数组内的索引是 key 的二次 hash 值的高 m 位
    • 假设小数组长度是 2的n次方,key 在小数组内的索引是 key 的二次 hash 值的低 n 位
  • **扩容:**每个小数组的扩容相对独立,小数组在超过扩容因子时会触发扩容,每次扩容翻倍
  • Segment[0] 原型:首次创建其它小数组时,会以此原型为依据,数组长度,扩容因子都会以原型为准

在JDK1.8中:

  • 数据结构:Node 数组 + 链表 + 红黑树,数组的每个头节点作为锁,如果多个线程访问的头节点不同,则不会冲突。首次生成头节点时如果发生竞争,利用 CAS而非 syncronized,进一步提升性能
  • **并发度:**Node 数组有多大,并发度就有多大,与 1.7 不同,Node 数组可以扩容
  • **扩容条件:**Node 数组满 3/4 时就会扩容
  • **扩容单位:**以链表为单位从后向前迁移链表,迁移完成的将旧数组头节点替换为 ForwardingNode
  • 扩容时并发 get:
    • 根据是否为 ForwardingNode 来决定是在新数组查找还是在旧数组查找,不会阻塞
    • 如果链表长度超过 1,则需要对节点进行复制(创建新节点),怕的是节点迁移后 next 指针改变
    • 如果链表最后几个元素扩容后索引不变,则节点无需复制
  • 扩容时并发 put:
    • 如果 put 的线程与扩容线程操作的链表是同一个,put 线程会阻塞

    • 如果 put 的线程操作的链表还未迁移完成,即头节点不是 ForwardingNode,则可以并发执行

    • 如果 put 的线程操作的链表已经迁移完成,即头结点是 ForwardingNode,则可以协助扩容


ThreadLocal
  • ThreadLocal可以实现【资源对象】的线程隔离,让每个线程各用各的【资源对象】,避免争用引发的线程安全问题

  • ThreadLocal同时实现了线程内的资源共享

  • 多个不同线程来时可以完成线程隔离,多个相同线程来时可以避免重复


ThreadLocalMap
  • key 的 hash值统一分配

  • 初始容量为16,扩容因子为2/3,扩容容量翻倍

  • key索引冲突后用开放寻址法解决冲突

  • get了不存在的key,会添加此key 值为null

【补充】开放寻址法:解决哈希冲突的一种办法,它和拉链法有所相似,只是处理冲突的方式不一样。拉链法通过在冲突位置开链表解决开放寻址法通过往后顺次找空位置解决


ThreadLocalMap中的Key

ThreadLocalMap中的Key被设计为弱引用,原因如下:

Thread可能需要长时间运行(如线程池中的线程),如果Key不再使用,需要在内存不足时释放占用的内存


ThreadLocalMap的内存释放

  • 被动 GC 释放 key:

    • 仅是让 key 的内存释放,关联 value 的内存并不会释放( value会停留在内存)
  • 懒惰被动释放 value:

    • get key 时,发现是 null key,则释放其 value 内存
    • set key 时,会使用启发式扫描,清除临近的 null key 的 value 内存,启发次数与元素个数,是否发现 null key 有关
  • 主动 remove 释放 key,value:

    • 会同时释放 key,value 的内存,也会清除临近的 null key 的 value 内存

    • 推荐使用它,因为一般使用 ThreadLocal 时都把它作为静态变量(即强引用),因此无法被动依靠 GC 回收

提升YOLOv8性能:用Swin Transformer替换Backbone的详细实现与分析【YOLOv8】 YOLOv8作为目标检测领域的最新版本,在速度和精度之间取得了良好的平衡。然而,随着计算机视觉任务的复杂性不断增加,YOLOv8的标准主干网络(Backbone)在处理高分辨率图像或多尺度目标时,可能存在一定的局限性。为了进一步提升YOLOv8的性能,我们可以引入Swin Transformer替换其主干网络,以增强模型的全局感知能力和多尺度特征提取能力。本文将详细介绍如何在YOLOv8中用Swin Transformer替换原有的Backbone,提供代码示例,并深入探讨其中的原理和效果。 阅读详情

相关推荐

第23章:代码迁移——用AI将Vue2迁移到Vue3、将JS迁移到TS

> - 用AI自动将Vue2 Options API转为Vue3 Composition API的完整流程 > - 将JavaScript项目批量迁移到TypeScript,自动添加类型推导 > - 处理依赖冲突、破坏性变更和迁移后的验证方法 > - 一个完整的Vue2到Vue3迁移案例(含路由、状态管理) > - 我如何用AI将公司一个10万行代码的老项目从JS升级到TS

sfishfly的博客 182

为什么Java每个对象都有wait()/notify()方法?

为什么80%的码农都做不了架构师?>>> ...

weixin_34203426的博客 533

【tessy】软件环境配置:Keil C51 Keil uVision UVSC (Default)

本文详细说明了tessy软件如何配置【Keil C51 Keil uVision UVSC (Default)】环境,具体操作步骤和注意

qq_37282365的博客 570

多线程基础之wait和notify

wait( ),notify( ),notifyAll( )都不属于Thread类,而是属于Object类,也就是每个对象都有wait( ),notify( ),notifyAll( ) 的功能,因为每个对象都有锁,锁是每个对象的基础 当需要调用以上的方法的时候,一定要对竞争资源进行加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常,也就是说需要配合sy...

张育嘉的博客 362

ThreadLocal

初始容量 16,扩容因子 2/3,扩容容量翻倍 (元素个数为 10 的时候就进行扩容了) key 索引冲突后用开放寻址法解决冲突(开放寻址法:计算出对应的位置后,如果冲突就找其位置后面的位置,如果为空则放入,不为空依次向后找直到符合为止)...

weixin_44711823的博客 244

Java多线程

Java多线程入门 本文主要介绍java多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。(进程是资源分配的最小单位,操作系统中运行的一个个软件就是一个个进程) 线程:同一进程中的线程共享代码和数据空间,每个线程有独立的运...

wmingstar 314

Java 多线程

一、首先讲解 Volatile 关键字,上Demo,大家思考下,"子线程结束"这句话能不能打印呢? public class VolatileTest { public static void main(String[] args) { MyVolatileRunnable myVolatileRunnable = new MyVolatileRunnable(); new Thread(myVolatileRunnable).start(); try {

weixin_39413778的博客 165

java多线程|提升java能力

java多线程|提升java能力

lch_blog 178

java多线程使用集合那

线程不安全的: ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺就是插入删除时非常麻烦 LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引  但是缺就是查找非常麻烦 要丛第一个索引开始 hashMap HashMap可以让你将空值作为一个表的条目的key或value。HashMap中

kuanghongjiang的专栏 675

Java多线程(一)

一:相关概念: 1.并行与并发: 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。        2.进程与线程 进程就是程序的一次执行过程(进程是一个动态过程 包括:为...

qq_31457665的博客 345

java 线程集合_java多线程使用集合那

线程不安全的: ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺就是插入删除时非常麻烦 LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引 但是缺就是查找非常麻烦 要丛第一个索引开始 hashMap HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一...

weixin_42421284的博客 173

多线程(二)

volatile可以保证共享变量的可见性,强制线程从主内存中读取变量值.相较于普通共享变量,volatile修饰的共享变量可以保证贡献变量的可见性 volatile不保证原子性 和方法 由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序. 注意 和方法的使用必须搭配同步代码块使用. 脱离使用会直接抛出异常,因为没有获取锁的对象 (通俗的说,你都没有对象获取锁,你让谁等待呢?) notify方法只是唤醒某一个等待线程.

m0_51435111的博客 247

JAVA多线程的一些注意

(整理自网上的各种博客,仅供个人学习)1.多线程的作用: (1)发挥cpu的优势,多核cpu (2)防止阻塞,在单核cpu上使用多线程会造成资源的浪费,但是仍然需要使用,以备单个线程阻塞后不影响其他任务的执行 方便建模,将大的任务分解为多个小的任务。2.创建线程的方式: 一般就是两种: (1)继承Thread类 (2)实现Runnable接口 至于哪个好,不用说肯定是后者好,因

u014571011的博客 805

Java多线程的那些

说明 Java的特有很多,像简单、易学、面向对象语言、健壮性、高性能 、分布式和多线程等,现在我们就来讲讲其中的一个特性----多线程 1、在说线程之前我们先来看看和线程息息相关的—进程 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。 系统运行一个程序即是一个进程从创建,运行到消亡的过程。 在Java中,当我们启动main函数时其实就是启动JVM的进程,而main函数所...

Fight Bravely的博客 232

多线程那些

多线程技术-Java实现

weixin_52938172的博客 1384

Java多线程编程--Thread类的那些--总结篇

本篇主要总结了多线程中的使用.详解了Thread这个类中使用技能.如:创建一个线程的几种方式,和能获取线程执行结果的方式,线程组的介绍,线程的启动方式start()方法的详解Thread类中常见的属性和方法,sleep(),join()方法的介绍和应用,如何提前中断一个线程,线程又有那些状态转换等等.

weixin_74742634的博客 2639

多线程(一)

线程并行执行 问题:如果上锁后,那多线程不就变成单线程了吗?我们只是在两个线程修改同一变量的时候, 才会加锁,如果有其他线程只是读取这个变量,那是没有影响的.如果我们顺序执行每个线程,会不会打印main方法呢?答案:不会,要等3个线程全部执行完才会打印主方法.在IDEA终端中使用jconsole命令可以查看当前程序线程的情况覆写run方法(线程的核心工作任务方法) 实现Runnable接口 覆写run方法 覆写Callable接口,覆写call方法 使用线程池创建线程 前两种方式启动线程都是通过Thre

m0_51435111的博客 564

Java面试那——基础200115

1. 什么是锁消除?什么是锁膨胀 锁消除: 对数据进行逃逸分析。对象实例都是存在于线程共享的堆中的,即便是局部变量的对象,也是存在于堆中,但是局部变量对象的引用是存在于方法栈中的,方法栈是线程私有,线程之间彼此不可见,当对于这样的引用进行加锁和释放锁的时候,其实是没有必要的,因为数据是不会逃逸出去,比如说StringBuffer的append方法,是synchronized修饰的同步方法,虚拟机检...

程序员世杰 529

Java面试那——基础200113

1.HashMap 和 Hashtable 有什么区别?HashMap 和 HashSet 呢? HashMap 和 Hashtable 区别: hashmap线程不安全、hashtable线程安全 hashmap继承自abstractmap、hashtable继承dictionary hashmap允许存储null键值(存一个null键和多个value是null)、hashtable不允许 h...

程序员世杰 3215

Java多线程】3——Lock API控制多线程

在实际场景中,读操作不会改变数据,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。针对这种场景,Java 的并发包提供了读写锁,它表示两个锁,一个是读操作相关的锁,称为读锁,这是一种共享锁;一个是写相关的锁,称为写锁,这是一种排他锁,也叫独占锁、互斥锁。

一棵灬大树的博客 1100

【手把手带你学Java EE】多线程那些,你了解了吗?

嗨~~上回咱们大致了解了计算机的工作原理,那么作为程序员,有一个东西不得不了解,那就是线程。所以,今天我们就来看看关于线程的那些情吧!

Stella-F的博客 1994

Java多线程--Thread类的那些 1.

isDemon();上述创建一个线程去执行i循环,main线程执行n循环中,每次的执行顺序是不一样的,线程会抢占式执行,就是当前cpu在执行一个线程时,线程调度程序会调度另一个线程在这个cpu上执行,原本在cpu上执行的那个线程会暂停执行即进入阻塞,等待下一次的调度.不过在多个线程一起运行时他们的执行的先后顺序是不确定的,这是由操作系统内核的线程调度程序中的"调度器"进行调度,但种调度类似于一种"随机调度"抢占式执行,这样就使线程的执行顺序不确定,执行的时间也不好确定.如上述的代码运行结果。

weixin_74742634的博客 1153

WordPress主题付费下载资源 Modown-V9.1

WordPress主题付费下载资源 Modown_V9.1

(完整word版)毕业设计三层电梯PLC控制系统设计.doc

(完整word版)毕业设计三层电梯PLC控制系统设计.doc

上一篇: 负载均衡 SpringCloud——Ribbon
Soil77
博客等级 码龄6年 1粉丝 · 2原创
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值