Java8中JUC包同步工具类深度解析(Semaphore,CountDownLatch,CyclicBarrier,Phaser)
个人主页: 进朱者赤
阿里非典型程序员一枚 ,记录平平无奇程序员在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名)
引言
在Java中,并发编程一直是一个重要的领域,而JDK 8中的java.util.concurrent(JUC)包提供了丰富的同步工具类,帮助开发者更加高效地处理并发问题。本文将分层次、分逻辑地介绍这些同步工具类的底层实现原理、使用方法和源码解析,并给出使用注意事项。
一、Semaphore(信号量)
1. 简介
Semaphore是一种同步工具,它允许一定数量的线程同时访问共享资源。通过控制信号量的许可数量,Semaphore能够实现对共享资源的并发访问限制。
2. 适用场景
Semaphore适用于需要限制并发访问共享资源数量的场景。例如,数据库连接池中的连接数控制,防止过多的请求同时访问数据库;或者在分布式系统中限制某个服务能够处理的并发请求数,以保证服务的稳定性和响应速度。
3. 使用
Semaphore semaphore = new Semaphore(5); // 初始化信号量为5
semaphore.acquire(); // 获取一个许可,若信号量为0则阻塞
// 访问共享资源
semaphore.release(); // 释放一个许可
4. 内部原理及源码解读
内部原理
Semaphore基于AQS(AbstractQueuedSynchronizer)实现,它维护了一个许可计数器。当线程调用acquire()
方法时,如果许可计数器大于0,则直接返回;否则线程会被加入等待队列并阻塞。当线程调用release()
方法时,许可计数器加一,并尝试唤醒等待队列中的一个线程。
源码解读
Semaphore内部有一个类Sync,它继承了AbstractQueuedSynchronizer。Sync有两个子类:FairSync和NonfairSync,分别用于处理公平和非公平策略。
// Semaphore的构造方法
public Semaphore(int permits) {sync = new NonfairSync(permits);
}public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
在NonfairSync
或FairSync
中,会重写AQS的tryAcquire
和tryRelease
等方法,来实现对许可计数器的增减操作以及线程的同步。
5. 注意事项
- 使用Semaphore时,要确保释放的许可数量与获取的数量相匹配,避免造成死锁或资源泄漏。
- 在高并发场景下,要合理设置信号量的初始值,以平衡资源利用率和并发性能。
二、CountDownLatch(倒计时锁)
1. 简介
CountDownLatch是一种同步工具,它允许一个或多个线程等待其他线程完成操作。通过维护一个计数器,当计数器减至0时,等待的线程将被唤醒。
2. 适用场景
CountDownLatch适用于需要等待一组线程完成某个任务后再继续执行的场景。例如,在启动多个线程进行并行计算时,可以使用CountDownLatch来等待所有线程计算完成后,主线程再进行汇总处理。
3. 使用
CountDownLatch latch = new CountDownLatch(5); // 初始化计数器为5
// ...其他线程执行操作,每完成一个操作调用latch.countDown()
latch.await(); // 当前线程等待,直到计数器减至0
4. 内部原理及源码解读
内部原理
CountDownLatch同样基于AQS实现,它维护了一个计数器。当线程调用countDown()
方法时,计数器减一;当计数器减至0时,AQS会唤醒等待队列中的所有线程。
源码解读
CountDownLatch的核心在于AQS的state变量,它代表了计数器的值。
// CountDownLatch的构造方法
public CountDownLatch(int count) {// 初始化计数器sync = new Sync(count);
}// Sync是CountDownLatch的内部类,继承了AQS
private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 498226498192269037L;Sync(int count) {setState(count); // 设置AQS的state为初始计数器值}// ...其他方法,如tryAcquireShared等
}
在tryAcquireShared
方法中,会检查计数器的值是否为0,如果是则直接返回表示可以获取共享资源,否则将当前线程加入等待队列。当countDown
方法被调用时,会调用releaseShared
方法减少计数器的值,并尝试唤醒等待队列中的线程。
5. 注意事项
- 在使用CountDownLatch时,要确保所有需要等待的线程都调用了
countDown()
方法,并且计数器的初始值设置正确。 - 等待线程在调用
await()
方法后会被阻塞,直到计数器减至0,因此要避免在等待过程中执行耗时操作或阻塞操作。
三、CyclicBarrier(循环栅栏)
1. 简介
CyclicBarrier是一种同步工具,它允许一组线程互相等待,直到所有线程都到达某个公共屏障点(barrier point)。一旦所有线程都到达屏障点,它们可以继续执行后续操作。
2. 适用场景
CyclicBarrier适用于需要将一组线程分割成多个阶段,并在每个阶段完成后进行汇总或协调的场景。例如,在多个线程协同完成一个复杂任务时,每个线程负责不同的子任务,当所有线程都完成各自子任务后,再进行下一步操作。
3. 使用
CyclicBarrier cyclicBarrier = new CyclicBarrier(5); // 初始化栅栏,需要5个线程到达
// ...多个线程执行操作,到达屏障点时调用cyclicBarrier.await()
cyclicBarrier.await(); // 当前线程等待,直到所有线程都到达屏障点
4. 内部原理及源码解读
内部原理:
CyclicBarrier内部使用了锁和条件变量
来实现线程间的同步。当线程到达屏障点时,首先检查是否有足够的线程到达,如果有则继续执行;否则将线程加入等待队列并阻塞。当最后一个线程到达屏障点时,唤醒所有等待的线程。
源码解读:
CyclicBarrier的核心在于其内部类Generation
,它代表了屏障的某个周期。每个Generation都有一个计数器来记录到达屏障点的线程数量。
// CyclicBarrier的构造方法
public CyclicBarrier(int parties, Runnable barrierAction) {this.parties = parties;this.count = parties;this.barrierCommand = barrierAction;this.lock = new ReentrantLock();this.condition = lock.newCondition();this.generation = new Generation();
}// Generation内部类
private static class Generation {boolean broken = false;int index = 0;
}
在await
方法中,线程会首先尝试获取锁,然后检查当前Generation的计数器是否为0。如果不为0,则线程会加入等待队列并阻塞。当最后一个线程到达屏障点时,它会修改Generation的计数器并唤醒等待队列中的所有线程。
5. 注意事项
- 在使用CyclicBarrier时,要确保所有线程都正确调用了
await()
方法,并且屏障点的线程数量设置正确。 - 如果在等待过程中发生异常或中断,CyclicBarrier可能会处于不一致状态,因此需要妥善处理异常和中断情况。
四、Phaser(阶段执行器)
1. 简介
Phaser是一种更灵活的同步工具,它提供了对一组线程进行分阶段同步的能力。Phaser允许线程注册、到达、等待和触发不同的阶段,非常适合用于需要动态管理线程阶段执行的场景。
2. 适用场景
Phaser适用于那些需要将线程划分为多个阶段,并在每个阶段结束时执行特定操作的情况。例如,在多阶段任务中,每个阶段可能需要不同的线程数量,且阶段的完成条件可能不同。使用Phaser,可以方便地对这些阶段进行管理和协调。
3. 使用
使用Phaser时,首先需要创建一个Phaser实例,并注册参与线程。然后,在每个阶段,线程可以调用arriveAndAwaitAdvance()
方法来表示它们已经完成了当前阶段的工作,并等待其他线程完成。当所有线程都到达当前阶段时,Phaser会触发阶段转换,并允许线程进入下一个阶段。
4. 内部原理及源码解读
内部原理
Phaser内部维护了一个复杂的状态机,包括当前阶段数、已注册的参与者数量、已到达的参与者数量等。每个线程在Phaser中都有一个到达点,当所有线程都到达当前阶段时,Phaser会触发阶段转换,并允许线程进入下一个阶段。
源码解读
Phaser的源码相对复杂,它涉及到了大量的状态和计数器管理。其中,register
方法用于注册参与者,arriveAndAwaitAdvance
方法用于表示线程到达当前阶段并等待其他线程。在arriveAndAwaitAdvance
方法中,会检查当前阶段是否已经完成,如果没有则增加已到达的参与者数量,并可能触发阶段转换。
深入理解Phaser的实现原理,查看和分析其源码是非常有帮助的。由于Phaser的源码较长且复杂,这里我聚焦于其核心机制,而不是完整的实现细节。
public class Phaser {// 表示参与者的数量,以及到达的参与者数量等状态信息private final AtomicLong state;// 用于等待/通知的锁private final Object lock;// 构造函数,初始化Phaserpublic Phaser() {state = new AtomicLong(Phaser.INITIAL_STATE);lock = new Object();}// 注册一个新的参与者,或者为已注册的参与者增加数量public void register() {// ... 省略具体的实现细节 ...}// 参与者到达某个阶段,并可能等待其他参与者public int arrive() throws InterruptedException {// ... 省略具体的实现细节 ...return phase;}// 参与者到达并等待其他参与者,同时推进到下一个阶段public int awaitAdvance(int phase) throws InterruptedException {// ... 省略具体的实现细节 ...return nextPhase;}// ... 其他方法,如deregister, arriveAndDeregister, bulkRegister, getPhase, getRegisteredParties等 ...// 内部状态表示,包含参与者数量和当前阶段等信息private static final long UNSET = -1L; // 用于表示未设置的值private static final long TERMINATED = Long.MAX_VALUE; // 表示Phaser已经终止private static final int MAX_PHASE = Integer.MAX_VALUE; // 最大阶段数private static final int PARTIES_MASK = 0xffff; // 参与者数量的掩码private static final int PHASE_MASK = ~PARTIES_MASK; // 阶段数的掩码private static final long INITIAL_STATE = (UNSET & PHASE_MASK) | (0 & PARTIES_MASK); // 初始状态// ... 其他内部方法和变量 ...
}
上面的代码只是一个框架,实际的Phaser实现要复杂得多。不过,通过这个框架,我们可以了解Phaser的一些核心组成部分:
-
状态维护:Phaser使用一个AtomicLong类型的state变量来维护其内部状态。这个状态包含了当前阶段数、已注册的参与者数量以及已到达的参与者数量等信息。通过使用位操作和掩码,Phaser能够在单个原子变量中高效地存储和更新这些信息。
-
注册与到达:register()方法用于注册新的参与者或增加已注册参与者的数量。arrive()方法用于表示参与者已经完成了当前阶段的工作,并可能等待其他参与者。这些方法会更新state变量中的相应信息,并根据需要唤醒等待的线程。
-
等待与推进:awaitAdvance()方法用于等待其他参与者到达当前阶段,并一起进入下一个阶段。这个方法会根据state变量的状态来决定是否需要阻塞调用线程。当所有参与者都到达当前阶段时,Phaser会更新state变量以推进到下一个阶段,并唤醒所有等待的线程。
-
中断与超时:实际的Phaser实现还支持响应中断和超时。这意味着如果线程在等待过程中被中断或超过指定的等待时间,它可以从等待状态中退出。这些特性是通过在内部使用锁和其他同步机制来实现的。
5. 注意事项
- 在使用Phaser时,需要确保正确管理线程的注册和注销,避免在阶段转换时出现不一致的情况。
- Phaser的灵活性也带来了一定的复杂性,因此在使用时需要深入理解其工作原理和使用方法,以避免出现错误或性能问题。
总结
横向对比
以下是以表格形式总结的JDK 8中JUC包中的Semaphore、CountDownLatch、CyclicBarrier和Phaser这四个同步工具类:
工具类 | 主要用途 | 内部原理 | 使用场景 |
---|---|---|---|
Semaphore | 控制访问某个或多个共享资源的线程数量 | 基于AQS实现,维护一个许可计数器 | 需要限制并发访问共享资源的场景,如连接池、线程池等 |
CountDownLatch | 允许一个或多个线程等待其他线程完成操作 | 基于AQS实现,维护一个计数器 | 用于协调一组线程的执行顺序,例如启动多个线程并行处理任务,并在所有任务完成后执行汇总操作 |
CyclicBarrier | 让一组线程互相等待,直到所有线程都到达某个公共屏障点 | 使用锁和条件变量实现,维护屏障的周期和计数器 | 需要一组线程在某个点相互等待的场景,如并行计算中的初始化、数据准备等 |
Phaser | 提供对一组线程进行分阶段同步的能力 | 维护复杂的状态机,包括阶段数、参与者数量和到达点 | 适用于需要将线程划分为多个阶段,并在每个阶段结束时执行特定操作的场景,如多阶段任务处理 |
常见面试题
在面试中,关于JDK 8中JUC包中Semaphore、CountDownLatch、CyclicBarrier和Phaser这四个同步工具类的使用场景,可以提出以下面试题:
Semaphore使用场景面试题:
- 请描述一个你曾经使用Semaphore解决并发问题的场景。你是如何确定需要的许可数量的?
- 在高并发环境下,如何使用Semaphore来限制对某个共享资源的访问数量?
CountDownLatch使用场景面试题:
- 假设你正在开发一个需要等待多个线程完成初始化操作的系统,你会如何使用CountDownLatch来实现?
- 请分享一个你使用CountDownLatch协调多个线程执行顺序的实例,并解释其工作原理。
CyclicBarrier使用场景面试题:
- 描述一个适合使用CyclicBarrier的场景,并解释为什么它比其他同步工具类更适合这个场景。
- 在一个多线程任务中,你需要在所有线程都完成某个阶段后才能进行下一阶段,你会如何使用CyclicBarrier来实现?
Phaser使用场景面试题:
- 请描述一个需要使用Phaser进行分阶段同步的场景,并解释Phaser在这个场景中的优势。
- 假设你正在开发一个复杂的多阶段任务,每个阶段需要不同数量的线程来完成,你会如何使用Phaser来管理这些线程的执行?
这些面试题旨在了解候选人对这些同步工具类应用场景的理解以及实际应用经验。通过回答这些问题,候选人可以展示他们对并发编程和JUC工具类的熟悉程度,以及解决实际问题的能力。
这些工具类都提供了灵活的同步机制,可以帮助开发者更好地控制和管理并发程序的执行。根据具体的使用场景和需求,可以选择合适的工具类来实现线程同步和协调。
以上就是JDK 8中JUC包中Semaphore、CountDownLatch、CyclicBarrier和Phaser这四个同步工具类的详细介绍。每个类都有其独特的使用场景和内部原理,了解并正确使用这些工具类,可以大大提高并发编程的效率和稳定性。
欢迎一键三连(关注+点赞+收藏)
,技术的路上一起加油!!!代码改变世界
- 关于我:阿里非典型程序员一枚 ,记录平平无奇程序员在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(
公众号同名
)⬇️欢迎关注下面的公众号:
进朱者赤
,认识不一样的技术人。⬇️
相关文章:
Java8中JUC包同步工具类深度解析(Semaphore,CountDownLatch,CyclicBarrier,Phaser)
个人主页: 进朱者赤 阿里非典型程序员一枚 ,记录平平无奇程序员在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名) 引言 在Java中,并发编程一直是一个重要的领域,而JDK 8中的java.u…...
岛屿个数(dfs)
[第十四届蓝桥杯省B 岛屿个数] 小蓝得到了一副大小为 M N MN MN 的格子地图,可以将其视作一个只包含字符 0 0 0(代表海水)和 1 1 1(代表陆地)的二维数组,地图之外可以视作全部是海水,每个岛…...
【C++造神计划】运算符
1 赋值运算符 赋值运算符的功能是将一个值赋给一个变量 int a 5; // 将整数 5 赋给变量 a 运算符左边的部分叫作 lvalue(left value),右边的部分叫作 rvalue(right value) 左边 lvalue 必须是一个变量 右边 rval…...
Cortex-M3/M4处理器的bit-band(位带)技术
ARM Cortex-M3/M4的位带(Bit-Band)技术是一种内存映射技术,它允许对单个位进行直接操作,而不需要对整个字(通常是32位)进行操作。这项技术主要用于对特定的位进行高效的读写,特别是在需要对GPIO…...
【TOP】IEEE旗下1区,影响因子将破8,3个月录用,CCF推荐,性价比高!
计算机类 ● 好刊解读 IEEE出版社、中科院2区TOP,CCF推荐,今天推荐的期刊可谓buff叠满,好刊质量靠谱,有意向评职晋升毕业作者可重点关注: 01 期刊简介 ✅出版社:IEEE ✅影响因子:7.5-8.0 ✅…...
赚钱游戏 2.0.1 版 (资源免费)
没有c编辑器的可以直接获取资源来玩 #include <iostream> #include <string> #include <windows.h> #include <conio.h> #include <fstream> #include <ctime> #include <time.h> #include <stdio.h> #include <cstring&g…...
服务调用-微服务小白入门(4)
背景 各个服务应用,有很多restful api,不论是用哪种方式发布,部署,注册,发现,有很多场景需要各个微服务之间进行服务的调用,大多时候返回的json格式响应数据多,如果是前端直接调用倒…...
代码随想录算法训练营第三十六天| 435. 无重叠区间、 763.划分字母区间、56. 合并区间
435 题目: 给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。 题目链接:435. 无重叠区间 - 力扣(LeetCode) 思路: …...
【AIGC调研系列】rerank3是什么
Rerank 3是一个针对企业搜索和检索辅助生成(RAG)系统优化的新型基础模型,它支持多语种、多结构数据搜索,并提供高精度的语义重排。通过这种方式,Rerank 3能够大幅提升响应准确度和降低延迟,同时大幅降低成本…...
Linux下网络编程基础知识--协议
网络基础 这一个课程的笔记 相关文章 协议 Socket编程 高并发服务器实现 线程池 协议 一组规则, 数据传输和数据的解释的规则。 比如说依次发送文件的文件名, 文件的大小, 以及实际的文件, 这样规定发送一个文件的顺序以及发送的每一个部分的格式等可以算是一种协议 型协议 …...
在 VS Code 中使用 GitHub Copilot
Code 结合使用。 GitHub Copilot 是什么 GitHub Copilot 是一个可以帮助你更简单、更快速地编写代码的工具,由 GPT-3 提供支持。你只需编写所需代码的描述——例如,编写一个函数来生成一个随机数,或对一个数组进行排序——Copilot 就会为你…...
使用spring-ai快速对接ChatGpt
什么是spring-ai Spring AI 是一个与 Spring 生态系统紧密集成的项目,旨在简化在基于 Spring 的应用程序中使用人工智能(AI)技术的过程。 简化集成:Spring AI 为开发者提供了方便的工具和接口,使得在 Spring 应用中集…...
免费的 ChatGPT 网站(六个)
🔥博客主页: 小羊失眠啦. 🎥系列专栏:《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞👍收藏⭐评论✍️ 文章目录 一、insCode二、讯飞星火三、豆包四、文心一言五、通义千问六、360智脑 现在智能…...
arm内核驱动-中断
先介绍个东西 ctags 这个工具可以像keil一样在工程里查找跳转,帮我们找到我们想要的东西。 安装教程可以找到,这里只讲怎么用。 在工程目录(包含所有你会用到的头文件等)下,先加载这个命令,可能要等待…...
第十五届蓝桥杯大赛软件赛省赛 C/C++ 大学 B 组
试题 C: 好数 时间限制 : 1.0s 内存限制: 256.0MB 本题总分:10 分 【问题描述】 一个整数如果按从低位到高位的顺序,奇数位(个位、百位、万位 )上 的数字是奇数,偶数位(十位、千位、十万位 &…...
kotlin编译版本
Kotlin和kapt的流行版本通常随着时间而变化,随着新版本的发布,更多的开发者会迁移到这些新版本。不过,由于Kotlin对向后兼容性的强调,大多数近期的Kotlin版本都支持Java 8。 截至本回答的知识截止日期(2023年ÿ…...
【C#】 删除首/尾部字符
代码 static void Main(string[] args){string str "123abc";string strdelete "abc";string str1 str.Trim(1);string strc str1.Trim(c);string str11 str1.TrimStart(1);string strcc str1.TrimEnd(c);string strabc str.Trim(strdelete.ToCharA…...
第十五篇【传奇开心果系列】Python自动化办公库技术点案例示例:深度解读Python 自动化处理图像在各行各业的应用场景
传奇开心果博文系列 系列博文目录Python自动化办公库技术点案例示例系列 博文目录前言一、行业应用场景介绍二、 **计算机视觉研究与开发示例代码**三、人工智能与机器学习示例代码四、医疗健康领域示例代码五、制造业与质量控制示例代码六、农业与环境科学示例代码七、电子商务…...
什么是MOV视频格式?如何把MP4视频转MOV视频格式?
一,前言 当然可以,MP4视频可以转换为MOV格式。这两种格式都是常见的视频文件格式,它们都可以用于存储和播放视频内容。虽然它们的编码方式和特性有所不同,但使用合适的视频转换工具可以轻松地将MP4视频转换为MOV格式。 二&#…...
整理的微信小程序日历(单选/多选/筛选)
一、日历横向多选,支持单日、双日、三日、工作日等选择 效果图 wxml文件 <view class"calendar"><view class"section"><view class"title flex-box"><button bindtap"past">上一页</button&…...
Unity 人形骨骼动画模型嘴巴张开
最近搞Daz3D玩,导入后挂上动画模型嘴巴张开,其丑无比。 Google了一下,得知原因是Unity没有对下巴那根骨骼做控制,动画系统就会把它放到默认的位置,嘴巴就张开了。找到了3种解决办法。 1.移除动画中对下巴这个骨骼的转…...
Python爬虫-京东商品评论数据
前言 本文是该专栏的第68篇,后面会持续分享python爬虫干货知识,记得关注。 在本专栏之前,笔者有详细介绍京东滑块验证码的解决方法,感兴趣的同学,可以直接翻阅文章《Python如何解决“京东滑块验证码”(5)》进行查看。 而本文,笔者以京东商品详情页的评论数据为例,通过…...
实况窗助力美团打造鸿蒙原生外卖新体验,用户可实时掌握外卖进展
自2023年华为宣布全新HarmonyOS NEXT蓄势待发,鸿蒙原生应用全面启动以来,已有金融、旅行、社交等多个领域的企业和开发者陆续宣布加入鸿蒙生态。其中,美团作为国内头部的科技零售企业,是首批加入鸿蒙生态的伙伴,其下的…...
Go语言mac环境搭建详解
Go语言mac环境搭建详解见视频,视频下方也有讲解具体的操作步骤。 Golang Mac电脑环境搭建、开发工具Vscode配置 Go语言mac环境搭建步骤如下: 1、下载安装Golang Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐&…...
房贷还款(C语言)
一、运行结果; 二、源代码; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h> # include <math.h>int main() {//初始化变量值;double m, r 0.01;float d 300000;float p 6000;//运算还款所需月份;m log10…...
系统架构最佳实践 -- 供应链系统架构
供应链系统是现代企业管理中不可或缺的一部分,它涉及到从原材料采购到产品销售的整个生产流程。一个高效的供应链系统可以帮助企业实现成本控制、库存优化和客户满意度提升等目标。在本文中,我们将讨论供应链系统的设计与实践。 一、供应链系统设计 业务…...
SpringCloudAlibaba-整合sentinel(四)
目录地址: SpringCloudAlibaba整合-CSDN博客 这里只关注代码部分,至于sentinel服务UI的实用,后面可以补上 这里做一个改造: 因为sentinel可以和openfeign结合使用,为微服务做熔断降级; 为了方便微服务之间…...
【研发日记】Matlab/Simulink软件优化(一)——动态内存负荷压缩
文章目录 背景介绍 初始代码 优化代码 分析和应用 总结 背景介绍 在一个嵌入式软件开发项目中,有一个使用MATLAB Function编写的算法模块,功能是从一个较大的数组中提取一段数据,然后求均值输出,示例如下: 初始代…...
python使用Flask框架开发API
Flask是一个基于Python的轻量级Web应用程序框架。 安装依赖库 pip install flask pip install werkzeug 上传接口 Python from flask import Flask, request from werkzeug.utils import secure_filenameapp Flask(__name__)app.route(/upload, methods[POST]) def uploa…...
使用hexo+gitee从零搭建个人博客
一、环境准备 1.Node.js:下载 | Node.js 中文网 (nodejs.cn) ,Hexo 是基于Node.js 的博客框架 教程:https://blog.csdn.net/weixin_52799373/article/details/123840137 node -v npm -v 安装 Node.js 淘宝镜像加速器 (cnpm&am…...
做网站的花费/网址域名注册信息查询
最近在一直做一个东西。设计一种脚本语言,再写一个翻译器,将这种脚本语言翻译成avr-gcc可以执行的C语言程序,再将得到的C语言程序利用avr-gcc编译器编译成Intel的hex文件格式,再写一个类似bootloader的东西,将这个hex文…...
wordpress内容/seo营销培训咨询
一、深入浅出理解索引结构 实际上,您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,…...
网站开发工资咋样/企业网站设计论文
转载于:https://www.cnblogs.com/ZHONGZHENHUA/p/6239597.html...
网站建设过程心得体会/江苏网站推广公司
比如你用local在栈上定义了一个局部变量LocalVar,你知道实际的指令是什么么?一般都差不多像下面的样子: push ebp mov esp, ebp sub esp, 4 现在栈上就有了4各字节的空间,这就是你的局部变量。 接下来&#x…...
办公室装修风格图/竞价关键词优化软件
找到webpack.config.prod.js 在loaders增加 {test: /\.less$/,loader: ExtractTextPlugin.extract(style, css!less) },...
网站开发接口/优化网站找哪家
对自有品牌而言,寻找到靠谱且性价比高的制造商是成功的关键,如果你还不知道该如何寻找这样的制造商,那么建议你收藏本文以便参考。一、寻找供应商注意事项要使你的自有品牌理念变成现实,你得找到靠谱的供应商并与他们建立合作关系…...