关于线程:深入讲解并发中最致命的死锁

本文首发自[慕课网](imooc.com) ,想理解更多IT干货内容,程序员圈内热闻,欢送关注"慕课网"及“慕课网公众号”!作者:王军伟Tech|慕课网讲师 1. 前言本节内容次要是对死锁进行深刻的解说,具体内容点如下: 了解线程的上下文切换,这是本节的辅助根底内容,从概念层面进行了解即可; 理解什么是线程死锁,在并发编程中,线程死锁是一个致命的谬误,死锁的概念是本节的重点之一; 理解线程死锁的必备 4 因素,这是防止死锁的前提,理解死锁的必备因素,能力找到防止死锁的形式; 把握死锁的实现,通过代码实例,进行死锁的实现,深刻领会什么是死锁,这是本节的重难点之一; 把握如何防止线程死锁,咱们可能实现死锁,也能够防止死锁,这是本节内容的外围。 2. 了解线程的上下文切换概述: 在多线程编程中,线程个数个别都大于 CPU 个数,而每个 CPU 同一时-刻只能被一个线程应用,为了让用户感觉多个线程是在同时执行的, CPU 资源的调配采纳了工夫片轮转的策略,也就是给每个线程调配一个工夫片,线程在工夫片内占用 CPU 执行工作。 定义: 以后线程应用完工夫片后,就会处于就绪状态并让出 CPU,让其余线程占用,这就是上下文切换,从以后线程的上下文切换到了其余线程。 问题点解析: 那么就有一个问题,让出 CPU 的线程等下次轮到本人占有 CPU 时如何晓得本人之前运行到哪里了?所以在切换线程上下文时须要保留以后线程的执行现场, 当再次执行时依据保留的执行现场信息复原执行现场。 线程上下文切换机会: 以后线程的 CPU 工夫片应用完或者是以后线程被其余线程中断时,以后线程就会开释执行权。那么此时执行权就会被切换给其余的线程进行工作的执行,一个线程开释,另外一个线程获取,就是咱们所说的上下文切换机会。 3. 什么是线程死锁定义:死锁是指两个或两个以上的线程在执行过程中,因抢夺资源而造成的相互期待的景象,在无外力作用的状况下,这些线程会始终互相期待而无奈持续运行上来。 如上图所示死锁状态,线程 A 己经持有了资源 2,它同时还想申请资源 1,可是此时线程 B 曾经持有了资源 1 ,线程 A 只能期待。 反观线程 B 持有了资源 1 ,它同时还想申请资源 2,然而资源 2 曾经被线程 A 持有,线程 B 只能期待。所以线程 A 和线程 B 就因为互相期待对方曾经持有的资源,而进入了死锁状态。 4. 线程死锁的必备因素 互斥条件:过程要求对所调配的资源进行排他性管制,即在一段时间内某资源仅为一个过程所占有。此时若有其余过程申请该资源,则申请过程只能期待;不可剥夺条件:过程所取得的资源在未应用结束之前,不能被其余过程强行夺走,即只能由取得该资源的过程本人来开释(只能是被动开释,如 yield 开释 CPU 执行权);申请与放弃条件:过程曾经放弃了至多一个资源,但又提出了新的资源申请,而该资源已被其余过程占有,此时申请过程被阻塞,但对本人已取得的资源放弃不放;循环期待条件:指在产生死锁时,必然存在一个线程申请资源的环形链,即线程汇合 {T0,T1,T2,…Tn}中的 T0 正在期待一个 T1 占用的资源,T1 正在期待 T2 占用的资源,以此类推,Tn 正在期待己被 T0 占用的资源。 ...

May 31, 2023 · 3 min · jiezi

关于线程:创建线程时实现hello-thread-具体用什么代码表示呀

实现hello thread 具体用代码示意为 public class HelloThread extends Thread!public void run()( Systen.cut.printin("Hello from a thread!"): }public statie vold main(string argsl)( (new HelloThreadi)).start(;) I残缺内容请点击下方链接查看:https://developer.aliyun.com/ask/463629?groupCode=learning 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

April 24, 2023 · 1 min · jiezi

关于线程:容易忽视的细节Log4j-配置导致的零点接口严重超时

作者:vivo 互联网服务器团队- Jiang Ye本文具体的记录了一次0点接口重大超时的问题排查经验。本文以作者本身视角极具代入感的描述了从问题定位到具体的问题排查过程,并通过根因剖析并最终解决问题。整个过程须要清晰的问题排查思路和丰盛的问题解决教训,也离不开公司弱小的调用链、和全方位的系统监控等基础设施。 一、问题发现我所负责的商城流动零碎用于承接公司线上官网商城的营销流动,最近忽然收到凌晨0点的服务超时告警。 营销流动类的零碎有如下特点: 营销流动个别会0点开始,如红包雨、大额优惠券抢券等。日常营销流动的机会刷新,如每日工作,每日签到,每日抽奖机会的刷新等。营销流动的利益刺激会吸引大量实在用户及黑产前来参加流动,所以流量在0点会迎来一波小顶峰,也正因如此线上偶现的服务超时告警起初并未引起我的留神。然而接下来的几天,每天的凌晨0点都会收到服务超时告警,这引起了我的警觉,决定一探到底。 二、问题排查首先通过公司的利用监控零碎查看了0点前后每分钟各接口的P95响应工夫。如下图所示,接口响应工夫在0点时刻最高达到了8s。持续查看锁定耗时最高的接口为商品列表接口,上面就针对这个接口开展具体的排查。 2.1 排查思路正式排查之前,先和大家分享下我对接口超时问题的排查思路。下图是一个简化的申请流程。 用户向利用发动申请应用服务进行逻辑解决应用服务通过RPC调用上游利用以及进行数据库的读写操作 服务超时可能是应用服务本身慢导致,也可能上游依赖响应慢导致。具体排查思路如下: 2.1.1 上游依赖慢服务排查(1)通过调用链技术定位上游依赖中的慢服务 调用链技术是实现零碎可观测性的重要保障,常见的开源计划有ziplin、pinpoint等。残缺的调用链能够按工夫正序记录每一次上游依赖调用的耗时,如rpc服务调用、sql执行耗时、redis拜访耗时等。因而应用调用链技术能够迅速定位到上游依赖的慢服务,如dubbo接口拜访超时、慢SQL等。但现实很饱满,事实很骨感。因为调用链路信息的数量过大,想要收集全量的链路信息须要较高的存储老本和计算资源。因而在技术落地时,通常都会采纳抽样的策略来收集链路信息。抽样带来的问题是申请链路信息的失落或不残缺。 (2)无调用链时的慢服务排查 如果调用链失落或不残缺,咱们就要再联合其它伎俩进行综合定位了。 上游RPC服务响应超时:如果是用Dubbo框架,在provider响应超时时会打印timeout相干日志;如果公司提供利用监控,还能够查看上游服务P95响应工夫综合判断。 慢SQL:MySQL反对设置慢SQL阈值,超过该阈值会记录下慢SQL;像咱们罕用的数据库连接池Druid也能够通过配置打印慢SQL日志。如果确认申请链路中存在慢SQL能够进一步剖析该SQL的执行打算,如果执行打算也没有问题,再去确认在慢SQL产生时mysql主机的零碎负载。 上游依赖蕴含Redis、ES、Mongo等存储服务时,慢服务的排查思路和慢SQL排查类似,在此不再赘述。 2.1.2 利用本身问题排查(1)应用逻辑耗时多 应用逻辑耗时多比拟常见,比方大量对象的序列化和反序列化,大量的反射利用等。这类问题的排查通常要从剖析源码动手,编码时应该尽量避免。 (2)垃圾回收导致的进展(stop the world) 垃圾回收会导致利用的进展,特地是产生Old GC或Full GC时,进展显著。不过也要看利用选配的垃圾回收器和垃圾回收相干的配合,像CMS垃圾回收器通常能够保障较短的工夫进展,而Parallel Scavenge垃圾回收器则是谋求更高的吞吐量,进展工夫会绝对长一些。 通过JVM启动参数-XX:+PrintGCDetails,咱们能够打印具体的GC日志,借此能够察看到GC的类型、频次和耗时。 (3)线程同步阻塞 线程同步,如果以后持有锁的线程长时间持有锁,排队的线程将始终处于blocked状态,造成服务响应超时。能够通过jstack工具打印线程堆栈,查找是否有处于block状态的线程。当然jstack工具只能采集实时的线程堆栈信息,如果想要查看历史堆栈信息个别须要通过Prometheus进行收集解决。 2.2 排查过程上面依照这个排查思路进行排查。 2.2.1 上游依赖慢服务排查(1)通过调用链查看上游慢服务 首先到公司的利用监控平台上,筛选出0点前后5min的调用链列表,而后依照链路耗时逆序排列,发现最大接口耗时7399ms。查看调用链详情,发现上游依赖耗时都是ms级别。调用链因为是抽样采集,可能存在链路信息失落的状况,因而须要其余伎俩进一步排查上游依赖服务。 (2)通过其余伎俩排查上游慢服务 接着我查看了0点前后的系统日志,并没有发现dubbo调用超时的状况。而后通过公司的利用监控查看上游利用P95响应工夫,如下图,在0点时刻,上游的一些服务响应时长的确会慢一些,最高的达到了1.45s,尽管对上游有肯定影响,但不至于影响这么大。 (3)慢SQL排查 接下来是慢SQL的排查,咱们零碎的连接池应用的是阿里开源的druid,SQL执行超过1s会打印慢SQL日志,查看日志核心也没有发现慢SQL的形迹。 到当初,能够初步排除因上游依赖慢导致服务超时,咱们持续排查利用本身问题。 2.2.2 利用本身问题排查(1)简单耗时逻辑排查 首先查看了接口的源码,整体逻辑比较简单,通过dubbo调用上游商品零碎获取商品信息,本地再进行商品信息的排序等简略的解决。不存在简单耗时逻辑问题。 (2)垃圾回收进展排查 通过公司利用监控查看利用的GC状况,发现0点前后没有产生过full GC,也没有产生过Old GC。垃圾回收进展的因素也被排除。 (3)线程同步阻塞排查 通过公司利用监控查看是否存在同步阻塞线程,如下图: 看到这里,终于有种天不负有心人的感觉了。从00:00:00开始始终到00:02:00,这两分钟里,呈现了较多状态为BLOCKED的线程,超时的接口大概率和这些blocked线程相干。咱们只须要进一步剖析JVM堆栈信息即可水落石出。 咱们随机选取一台比拟有代表性的机器查看block堆栈信息,堆栈采集工夫是2022-08-02 00:00:20。 // 日志打印操作,被线程catalina-exec-408阻塞"catalina-exec-99" Id=506 BLOCKED on org.apache.log4j.spi.RootLogger@15f368fa owned by "catalina-exec-408" Id=55204 at org.apache.log4j.Category.callAppenders(Category.java:204) - blocked on org.apache.log4j.spi.RootLogger@15f368fa at org.apache.log4j.Category.forcedLog$original$mp4HwCYF(Category.java:391) at org.apache.log4j.Category.forcedLog$original$mp4HwCYF$accessor$pRDvBPqB(Category.java) at org.apache.log4j.Category$auxiliary$JhXHxvpc.call(Unknown Source) at com.vivo.internet.trace.agent.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:46) at org.apache.log4j.Category.forcedLog(Category.java) at org.apache.log4j.Category.log(Category.java:856) at org.slf4j.impl.Log4jLoggerAdapter.info(Log4jLoggerAdapter.java:324) ... // 日志打印操作,被线程catalina-exec-408阻塞"catalina-exec-440" Id=55236 BLOCKED on org.apache.log4j.spi.RootLogger@15f368fa owned by "catalina-exec-408" Id=55204 at org.apache.log4j.Category.callAppenders(Category.java:204) - blocked on org.apache.log4j.spi.RootLogger@15f368fa at org.apache.log4j.Category.forcedLog$original$mp4HwCYF(Category.java:391) at org.apache.log4j.Category.forcedLog$original$mp4HwCYF$accessor$pRDvBPqB(Category.java) at org.apache.log4j.Category$auxiliary$JhXHxvpc.call(Unknown Source) at com.vivo.internet.trace.agent.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:46) at org.apache.log4j.Category.forcedLog(Category.java) at org.apache.log4j.Category.log(Category.java:856) at org.slf4j.impl.Log4jLoggerAdapter.warn(Log4jLoggerAdapter.java:444) ... // 日志打印操作,被线程catalina-exec-408阻塞"catalina-exec-416" Id=55212 BLOCKED on org.apache.log4j.spi.RootLogger@15f368fa owned by "catalina-exec-408" Id=55204 at org.apache.log4j.Category.callAppenders(Category.java:204) - blocked on org.apache.log4j.spi.RootLogger@15f368fa at org.apache.log4j.Category.forcedLog$original$mp4HwCYF(Category.java:391) at org.apache.log4j.Category.forcedLog$original$mp4HwCYF$accessor$pRDvBPqB(Category.java) at org.apache.log4j.Category$auxiliary$JhXHxvpc.call(Unknown Source) at com.vivo.internet.trace.agent.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:46) at org.apache.log4j.Category.forcedLog(Category.java) at org.apache.log4j.Category.log(Category.java:856) at org.slf4j.impl.Log4jLoggerAdapter.warn(Log4jLoggerAdapter.java:444) ...通过堆栈信息能够剖析出2点: ...

April 21, 2023 · 2 min · jiezi

关于线程:看完这篇线程线程锁与线程池讲解面试随便问

文:单线程——多线程的开启——线程锁——线程同步工具——手写连接池——连接池工具类。 一、线程1.线程的概念 2.线程与过程的关系 3.定义: 区别:如上!!! 4.wait()和sleep() 5.线程的状态及其他API 二、线程锁线程并发同步时,引入了锁机制。 一般锁机制:synchronized  润饰代码块与volatile  润饰成员变量 Lock!!共同点:都是从里面创立锁类、再把锁传到线程里对变量对象赋值。 (1)重入锁 (2)读写拆散锁 区别: 三、线程同步工具类!!共同点:都是从里面创立工具类、再把工具类的参数传到线程外面执行。 CountDowmLatch闭锁:期待所有线程执行完 CyclicBarrier栅栏:期待所有线程达到后开启 Exchanger交换机:交换数据 信号量(1)概念 (2)利用场景一 (3)利用场景二 四、线程池为什么应用线程池 线程池的外围队列阻塞式队列:只用于线程对象,次要用于引出线程池 手动创立线程池 Executors工具创立线程外围线程:0(长期线程)、1(队列)、N(队列) 定时线程: 五、彩蛋图

November 17, 2022 · 1 min · jiezi

关于线程:死锁产生的4个必要条件

死锁产生的4个必要条件1 . 产生死锁的必要条件:(1)互斥条件:过程要求对所调配的资源进行排它性管制,即在一段时间内某资源仅为一过程所占用。(2)申请和放弃条件:当过程因申请资源而阻塞时,对已取得的资源放弃不放。(3)不剥夺条件:过程已取得的资源在未应用完之前,不能剥夺,只能在应用完时由本人开释。(4)环路期待条件:在产生死锁时,必然存在一个过程–资源的环形链。解决死锁的根本办法 2 . 预防死锁:(1)资源一次性调配:一次性调配所有资源,这样就不会再有申请了:(毁坏申请条件)(2)只有有一个资源得不到调配,也不给这个过程调配其余的资源:(毁坏放弃条件)(3)可剥夺资源:即当某过程取得了局部资源,但得不到其它资源,则开释已占有的资源(毁坏不可剥夺条件)(4)资源有序调配法:零碎给每类资源赋予一个编号,每一个过程按编号递增的程序申请资源,开释则相同(毁坏环路期待条件)

October 18, 2022 · 1 min · jiezi

关于线程:线程基础知识

前言和纲要计算机系统里每个过程(Process)都代表着一个运行着的程序,过程是对运行时程序的封装,零碎进行资源调度和调配的根本单位。一个过程下能够有很多个线程,线程是过程的子工作,是CPU调度和分派的根本单位,用于保障程序的实时性,实现过程外部的并发,线程同时也是操作系统可辨认的最小执行和调度单位。在 Java 里线程是程序执行的载体,咱们写的代码就是由线程运行的。有的时候为了减少程序的执行效率,咱们不得不应用多线程进行编程,尽管多线程能最大化程序利用 CPU 的效率,但也是程序事变多发、程序员脱发的最大诱因。次要是平时咱们的思维默认是单线程的,写多线程的时候得能够切换一下才行,这就要求咱们对线程的基础知识理解的比拟透彻。这篇文章咱们总结一下 Java线程的根底,多把握点,当前就少掉点头发,不光省下植发的钱,工资还能往上涨,这么一想几乎双赢。本文的纲要如下: 好了让咱们开始明天的注释,间接上代码吧。Java 中的线程到目前为止,咱们写的所有 Java 程序代码都是在由JVM给创立的 Main Thread 中单线程里执行的。Java 线程就像一个虚构 CPU,能够在运行的 Java 应用程序中执行 Java 代码。当一个 Java 应用程序启动时,它的入口办法 main() 办法由主线程执行。主线程(Main Thread)是一个由 Java 虚拟机创立的运行你的应用程序的非凡线程。因为 Java 里所有皆对象,所以线程也是用对象示意的,线程是类 java.lang.Thread 类或者其子类的实例。在 Java 应用程序外部, 咱们能够通过 Thread 实例创立和启动更多线程,这些线程能够与主线程并行执行应用程序的代码。创立和启动线程在 Java 中创立一个线程,就是创立一个 Thread 类的实例 Thread thread = new Thread(); 启动线程就是调用线程对象的 start() 办法 thread.start(); 当然,这个例子没有指定线程要执行的代码,所以线程将在启动后立刻进行。指定线程要执行的代码有两种办法能够给线程指定要执行的代码。 第一种是,创立 Thread 的子类,笼罩父类的 run() 办法,在run() 办法中指定线程要执行的代码。第二种是,将实现 Runnable (java.lang.Runnable) 的对象传递给 Thread 构造方法,创立Thread 实例。 其实,还有第三种用法,不过细究下来可归类到第二种的非凡应用形式,上面咱们看看这三种的用法和区别。通过 Thread 子类指定要执行的代码通过继承 Thread 类创立线程的步骤: 定义 Thread 类的子类,并笼罩该类的 run() 办法。run() 办法的办法体就代表了线程要实现的工作,因而把 run 办法称为执行体。创立 Thread 子类的实例,即创立了线程对象。调用线程对象的 start 办法来启动该线程。 ...

July 1, 2022 · 2 min · jiezi

关于线程:专属云资源包计算规格探秘

咱们晓得专属云资源包有三种规格:36 核/321 GB 、44 核/380 GB、72 核/704 GB,很多人对资源包的规格不太了解,上面我带大家一步步进行分析。 首先咱们要晓得一个专属云计算资源包应着是一台物理机,订购一个资源包,就意味着独占一台物理机。三种规格中,36 核/321 GB对应着V3规格服务器 ,44 核/380 GB对应着V4规格服务器、72 核/704 GB对应着V5规格服务器。 既然是一台物理机,那么为何会有36 核/321 GB 、44 核/380 GB、72 核/704 GB这样奇怪的规格?还有,资源包中的核数到底指的是什么?之所以会有这么奇怪的规格,根本原因是当咱们将一台物理机纳入到云计算环境中,会存在虚拟化平台治理的须要,会吃掉一部分CPU和内存的资源,真正能给客户提供的计算资源就要减去这部分被吃掉的资源。 那么这些规格到底是怎么得来的,咱们能够通过给定的计算公式得出。在给出计算公式之前,须要明确资源包里的核其实指的是(超)线程核。 什么叫(超)线程核?一般来说一个CPU外围对应一个物理线程,但出于对多任务处理的须要,英特尔通过自家的超线程技术能够把一个物理线程模拟出两个逻辑线程来用,以充分发挥 CPU 性能,也就是说,单核心的CPU被模仿成了一个相似双核心CPU的性能,对应操作系统或者虚拟化平台来说,会把1个物理线程(外围)模拟出的2个逻辑线程当成2个CPU核看待。 专属云资源包核数(线程数)的计算公式如下:单计算节点可用线程数 = 单计算节点总线程数 – 单计算节点软件开销线程数单计算节点CPU总线程数 = CPU数量×CPU核数×CPU每核的线程数单计算节点软件开销线程数 = (物理服务器超线程数/10)(向上取偶数)+ FusionStorage Block开销  举例,对于V4通用计算类服务器,CPU规格为2路14核,超线程因子数为2,因而:单计算节点软件开销线程数 = 2x14x2/10(向上取偶)+ 6 = 6 + 6 = 12单计算节点可用CPU线程数 = 2x14x2 - 12 = 44哈,计算结果完全一致! 专属云资源包内存的计算公式如下:单计算节点可用内存 = 单计算节点总内存 – 单计算节点的虚拟化软件内存开销其中单计算节点的虚拟化软件内存开销,包含单计算节点的Domain0内存开销为56GB,Hypervisor治理开销为8GB,这样加起来就是64G。 举例,对于V4通用计算类服务器,总内存容量为14 x 32 = 448GB,因而对于单计算节点可用内存 = 448 - 64= 384GB*。*这里计算出的数据有4G的差别,阐明计算公式中的内存的额定开销也不齐全是固定值。通过以上的计算公式咱们能够再算一下采纳V5服务器的72 核/704 GB是怎么得来的。V5服务器的规格:222外围(英特尔至强金牌6161,超线程因子数为2),2432GB DIMM,2600G SAS,SR130,410GE NIC。单计算节点软件开销线程数 = 2x22x2/10(向上取偶)+ 6 = 10 + 6 = 16单计算节点可用CPU线程数 = 2x22x2 - 16 = 72单计算节点可用内存=24*32-64G=704G ...

April 2, 2022 · 1 min · jiezi

关于线程:全局变量和多线程

先抛出问题:有一全局变量,一个工作读,一个工作写,有没有问题?先抛出答案,不倡议这么做。 临界资源:各工作/线程采取互斥的形式,实现共享的资源称作临界资源。属于临界资源的硬件串口打印、显示等,软件有音讯缓冲队列、变量、数组、缓冲区等。多任务/线程间应采取互斥形式,从而实现对这种资源的共享。多任务/多线程状况下在写模块时,只须要封装进爱护机制即可。常见的爱护机制无关中断、信号量、互斥锁等。最近做的几个我的项目遇到的一些非凡场景: 1.校时定义全局变量 typedef struct{ /*应用软件工夫*/ unsigned int year; unsigned char month; unsigned char day; unsigned char hour; /*时 */ unsigned char minute; /*分 */ unsigned char second; /*秒 */ unsigned int millisecond; /*毫秒*/ unsigned int microsecond; /*微秒*/ unsigned int nanosecond; /*纳秒*/ unsigned int u40mic; int delta; /*日历时与本地RTC的差值*/ int delta_rtc; /*同步RTC与本地RTC的差值*/ int delta_year; /*用于操作系统年份保护*/ int timeElapse; /*零碎上电后运行了多少秒*/ char DateValid; /*年月日无效标识,1为无效*/ char TimeValid; /*时分秒无效标记,1为无效*/ char BusVaild; /*总线工夫无效, 1为无效, 用于飞参授时及文件夹创立*/}STRUCT_CORE_TIME;STRUCT_CORE_TIME gst_TimeState;int TIME_Regulate_Time(int year, int month, int date, int hour, int minute, int second){ struct timespec NewSetTime; struct tm DownTime; SYSTEM_TIME_TYPE sys_Time = 0; RETURN_CODE_TYPE retCode; if((year < 2000)||(year > 2255)) return -1; if((month < 1) || (month > 12)) return -2; if((date < 1) || (date>31)) return -3; if((hour > 23) || (minute > 59) || (second > 59)) return -4; /*当超出年限时,获取delta*/ if(year > 2100){ if(((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)){ gst_TimeState.delta_year = year - 2096; year = 2096; }else{ gst_TimeState.delta_year = year - 2100; year = 2100; } }else{ gst_TimeState.delta_year = 0; } GET_TIME(&sys_Time, &retCode); DownTime.tm_hour = hour; DownTime.tm_min = minute; DownTime.tm_sec = second; DownTime.tm_mday = date; DownTime.tm_mon = month - 1; DownTime.tm_year = year - 1900; DownTime.tm_isdst = 0; /*设置为非夏令时*/ NewSetTime.tv_sec = mktime(&DownTime); /*工夫间接用delta保护,不波及零碎工夫*/ gst_TimeState.delta = NewSetTime.tv_sec - sys_Time / Time_1S; /*LOG4C日志零碎工夫更新*/ sd_settimeofday(gst_TimeState.delta); gst_TimeState.TimeValid = 1; gst_TimeState.DateValid = 1; return 0;}void TIME_Gather_Time(){ struct timespec NewSetTime; struct tm DownTime; SYSTEM_TIME_TYPE sys_Time = 0; RETURN_CODE_TYPE retCode; GET_TIME(&sys_Time, &retCode); NewSetTime.tv_sec = sys_Time / Time_1S + gst_TimeState.delta; localtime_r(&NewSetTime.tv_sec, &DownTime); gst_TimeState.year = DownTime.tm_year + 1900 + gst_TimeState.delta_year; gst_TimeState.month = DownTime.tm_mon + 1; gst_TimeState.day = DownTime.tm_mday; gst_TimeState.hour = DownTime.tm_hour; gst_TimeState.minute = DownTime.tm_min; gst_TimeState.second = DownTime.tm_sec; gst_TimeState.millisecond = sys_Time / 1000000 % 1000; gst_TimeState.microsecond = sys_Time / 1000 % 1000; gst_TimeState.nanosecond = sys_Time % 1000; gst_TimeState.u40mic = ((gst_TimeState.hour * 3600 + gst_TimeState.minute * 60 + gst_TimeState.second)*1000 + gst_TimeState.millisecond)*25;}校时线程做如下的周期调用: ...

March 7, 2022 · 2 min · jiezi

关于线程:如何利用线程堆栈定位问题

01 背景针对在一些服务中会呈现的cpu飙高、死锁、线程假死等问题,总结和提炼排查问题的思路和解决方案十分重要。上述问题会波及到线程堆栈,本文将结合实际案例来论述一下线程堆栈的性能。 02 基本知识2.1 什么是线程堆栈线程堆栈是零碎过后某个时刻的线程运行状态(即霎时快照)。 线程堆栈的信息蕴含 线程的名字、ID、线程的数量线程的运行状态、锁的状态(锁被那个线程持有,哪个线程再期待锁)<!----> 调用堆栈(即函数的调用档次关系)。调用堆栈蕴含残缺的类名,所执行的办法,源代码的行数2.2 线程堆栈能剖析问题类型线程堆栈定位问题只能定位在以后线程上留下痕迹的问题 2.3 线程堆栈不能剖析的问题线程堆栈不能定位在线程堆栈上不留痕迹的问题: 并发bug导致的数据凌乱,这种问题在线程堆栈中没有任何航迹,所以这种问题线程堆栈无奈提供任何帮忙。数据库锁表的问题,表被锁,往往因为某个事务没有提交/回滚,但这些信息无奈在堆栈中体现呈现在线程上不留痕迹的问题只能通过其余伎俩来进行定位。在理论的零碎中,零碎的问题分为几种类型: 在堆栈中可能体现出问题的,应用线程堆栈进行定位无奈在线程中留下痕迹的问题定位,须要依赖于一个好的日志设计十分荫蔽的问题,只能依赖于丰盛的代码教训,如多线程导致的数据凌乱,以及前面提到的幽灵代码2.4 如何输入线程堆栈 kill -3 pid 命令只能打印那一瞬间java过程的堆栈信息,适宜在服务器响应慢,cpu、内存疾速飙升等异常情况下应用,能够不便地定位到导致异样产生的java类,解决如死锁、连贯超时等起因导致的零碎异样问题。该命令不会杀死过程。 备注: 将屏幕输入写入到文件中,重写文件内容 将屏幕输入增加到文件开端Linux常用命令 命令形容ps查找过程的pidpstack打印过程或者线程的栈信息strace统计每一步零碎调用破费的工夫2.5 剖析线程堆栈-线程状态的剖析2.5.1 概述2.5.2 Runnable从虚拟机的角度来看,线程处于正在运行的状态。 那么处于RUNNABLE的线程是不是肯定耗费CPU呢?实际上不肯定。上面的线程堆栈示意该线程正在从网络读取数据,只管上面这个线程显示为RUNNABLE状态,但实际上网 络IO,线程绝大多数工夫是被挂起,只有当数据达到之后,线程才被从新唤醒。挂起产生在本 地代码(Native)中,虚拟机基本不晓得,不像显式调用了Java的sleep()或者wait()等办法,虚构 机能晓得线程的真正状态,但对于本地代码中的挂起,虚拟机无奈真正地晓得线程状态,因 此它一律显示为RUNNABLE。像这种socket IO操作不会耗费大量的CPU,因为大多工夫在期待,只有数据到来之后,才耗费一点点CPU. Thread-39" daemon prio=1 tid=0x08646590 nid=0x666d runnable [5beb7000..5beb88b8] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.read(SocketInputStream.java:129) at java.io.BufferedInputStream.fill(BufferedInputStream.java:183) at java.io.BufferedInputStream.read(BufferedInputStream.java:201) - locked <0x47bfb940> (a java.io.BufferedInputStream) at org.postgresql.PG_Stream.ReceiveChar(PG_Stream.java:141) at org.postgresql.core.QueryExecutor.execute(QueryExecutor.java:68) - locked <0x47bfb758> (a org.postgresql.PG_Stream) at org.postgresql.Connection.ExecSQL(Connection.java:398)上面的线程正在执行纯Java代码指令,实实在在是耗费CPU的线程。 "Thread-444" prio=1 tid=0xa4853568 nid=0x7ade runnable [0xafcf7000..0xafcf8680] java.lang.Thread.State: RUNNABLE//实实在在再对应CPU运算指令at org.apache.commons.collections.ReferenceMap.getEntry(Unknown Source) at org.apache.commons.collections.ReferenceMap.get(Unknown Source)at org.hibernate.util.SoftLimitMRUCache.get(SoftLimitMRUCache.java:51) at org.hibernate.engine.query.QueryPlanCache.getNativeSQLQueryPlan()at org.hibernate.impl.AbstractSessionImpl.getNativeSQLQueryPlan()at org.hibernate.impl.AbstractSessionImpl.list()at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:164)at com.mogoko.struts.logic.user.LeaveMesManager.getCommentByShopId()at com.mogoko.struts.action.shop.ShopIndexBaseInfoAction.execute() ......上面的线程正在进行JNI本地办法调用,具体是否耗费CPU,要看TcpRecvExt的实现,如 果TcpRecvExt 是纯运算代码,那么是实实在在耗费CPU,如果TcpRecvExt()中存在挂起的代 码,那么该线程只管显示为RUNNABLE,但实际上也是不耗费CPU的。 ...

February 16, 2022 · 6 min · jiezi

关于线程:线程的创建方式安全状态

多线程明天咱们来聊聊多多线程 多线程创立形式通过继承Thread创立通过接口Runnable创立线程平安同步代码块同步办法Lock锁线程状态Thread与Runnable 创立Thread public class MyThread extends Thread { public MyThread(String name){ super(name); } public void run(){ for (int i = 0; i < 20; i++) { //getName()办法 来自父亲 System.out.println(getName()+i); } }}// 测试类 public class Demo { public static void main(String[] args) { System.out.println("这里是main线程"); MyThread mt = new MyThread("TR"); mt.start();//开启了一个新的线程 for (int i = 0; i < 20; i++) { System.out.println("MI:"+i); } }}Runnable public class MyRunnale implements Runnable{ @Override public void run(){ for(int i = 0 ; i < 20 ; i++){ System.out.println(Thread.cerrentThread().getName() + i); } }}// 测试类2 public class Demo02{ public static void mian(Stirng []args){ // 创立自定义类对象 线程工作对象 MyRunnable mr = new MyRunnable(); // 创立线程对象 Thread t = new Thread(mr,"Run对象"); t.start(); System.out.println("main的线程"); }}Thread和Runnable区别: 如果一个类继承Thread,他就不适宜资源共享,然而应用Runnable接口的话,则更容易实现资源共享. ...

September 1, 2021 · 2 min · jiezi

关于线程:多线程线程初体验

上节讲了下线程和过程的基础知识,然而对于Java来说,可能探讨线程的工夫会更多些,所以接下来的一系列文章都是着重在探讨线程。 创立线程创立的线程的形式是陈词滥调也是面试中喜爱问的问题之一了,网上的说法七嘴八舌,说什么实现Runnable接口和实现Callable接口是同一种类型,这种说法也不是说谬误,只不过须要看站在哪个角度看。然而这种细节其实没有必要太在意,不要钻牛角尖。 实现Runnable接口实现Runnable接口,而后重写run() 办法,该办法定义了线程的工作形式和工作内容。 public class ImplementsRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "------Runnable线程工作中。。。"); } public static void main(String[] args) { for (int i = 0; i < 50; i++) { new Thread(new ImplementsRunnable()).start(); } }}在main办法中,开启了50个线程运行,开启线程其实就是新建了一个Thread,而后把实现Runnable接口的类作为参数传进去,当初咱们来看看运行的后果 能够看到尽管咱们是依照程序来新建线程的,然而线程的先后执行程序是由CPU来管制的,能够说是不可控的,也正是这样能力阐明了多线程在运行。 实现Callable接口实现了接口后须要重写的是call() 办法 public class ImplementsCallable implements Callable { @Override public Object call() { System.out.println(Thread.currentThread().getName() + "--------callable线程工作中"); return "实现callable,有返回值"; } public static void main(String[] args) throws ExecutionException, InterruptedException { for (int i = 0; i < 50; i++) { ImplementsCallable callable = new ImplementsCallable(); FutureTask<String> task = new FutureTask<String>(callable); new Thread(task).start(); System.out.println(task.get()); } }}值得注意的是,在Thread类中的构造函数中,并没有参数为Callable的重载构造函数,基本上都是Runnable ...

June 25, 2021 · 3 min · jiezi

关于线程:简述CPU多核并发缓存架构JVM模型和JMM多线程内存模型

CPU多核并发缓存架构简述首先讲一下CPU的工作原理晚期的CPU性能低,并且是单核,CPU间接和主存交互,从主存读写数据间接实现。随着CPU的一直降级,主存和CPU的匹配越来越差,于是引入了缓存。 CPU读取数据首先查看寄存器中有没有他要的数据。如果寄存器没有,就在一级缓存查找。如果一级缓存没有,就在二级缓存查找。二级也没有,最初在主存中查找数据。留神:在最初和主存交互的时候有一点须要留神,因为这时有多个CPU须要从主存中取数据,CPU1取了数据,CPU2也取了数据,那么到底要存哪个数据?于是引入了缓存一致性协定MESI。这里不细谈这个。总之有了这个协定才能够保证数据的一致性。 JVM内存模型Java在运行程序的时候会主动将数据分成下面几个区域。蓝色代表线程的共享数据区域,绿色是各个线程的公有区域。 办法区:用于存储编译后的数据。包含:类信息、常量、动态变量还有运行时常量池(存储编译后的符号援用和编译生成的字面变量)JVM堆JVM内存最大的一块,次要用来为对象实例分配内存,还有他是垃圾回收治理的次要区域。程序计数器寄存以后线程的下一条操作的字节码行号,字节码解释器工作的时候读取这个行号,从而决定执行分支、循环、跳转、异样解决等性能。虚拟机栈随着线程的创立而呈现,每个办法执行的时候都会在这里记录他的参数信息,指针,返回值等信息。本地办法栈这部分次要与虚拟机用到的 Native 办法相干。 Java多线程内存模型概述(重要)这个模型是一个形象的,是一个标准。他规定了线程从内存中拜访变量的标准。首先JAVA会给每个线程分配内存用来为他们存储各自的公有数据,每个线程想要获取主存中的变量(共享变量),必须将这个变量复制一份到本人的工作内存,对他扭转后再放回主存中。具体拜访细节见图 Lock Unlock要想保障共享变量批改的一致性,每个线程在读取到数据后,必须给变量加锁,读取完之后再解锁。Read获取到主存中的数据,将他传送到线程工作的内存。Load在线程工作的内存中将获取的数据复制一份。Use如果线程计数器区读到了扭转数据相干的字节码操作数,就会对失去的正本数据传给执行引擎。Assign在虚拟机遇到赋值的字节码指令,将执行引擎传过来的值赋值给工作区内存的变量。Store将工作内存中的变量传给主内存。Write把获取到的工作变量写入到主内存中。图片起源:刘乃源讲义

June 9, 2021 · 1 min · jiezi

关于线程:再不解决延迟不当小心你的内存被打爆

摘要:这是在具体代码中发现的不当提早的问题,极其状况下可能把内存打爆。本文分享自华为云社区《线程中不当应用提早问题》,原文作者:技术火炬手 。 背景这是在具体代码中发现的不当提早的问题,极其状况下可能把内存打爆。 代码DevLicenseServiceRoaDelegateImpl.java 定义: 应用: signalRefreshHelp 定义 这段代码最大问题是应用提早算法不当,在极其状况下会导致内存暴涨,重大影响服务程序的性能。 不要应用sleep来实现提早应用sleep实现提早看起来十分直观,然而这个在高并发、多申请、长期运行的服务程序里必须特地小心。这是因为掂量服务程序性能的一个十分重要的指标是QPS, 就是服务程序的解决能力,个别状况下越大越好;服务程序的总并发能力等于每个线程的qps;单个线程的QPS = 1000毫秒 / (解决一个申请的毫秒);所以下面那个线程的QPS <= 1000 / 10000 = 0.1 (因为线程sleep了10000毫秒)。 这里的解决逻辑是谬误的!也有很重大的性能隐患,不过幸好调用这个api 申请不多,才没有导致重大问题。 开发者的用意是在创立一个工作后,提早10s执行该工作,解决时序图如下 如果工夫点t1 & t2 挨得很靠近的话,线程在执行job1 & job2 也是很靠近。 但理论的状况变成: 就算创立job1 & job2的工夫很靠近,但job2执行的工夫会比预期多了10s;间断提交的工作越多,越容易沉积,这些沉积的工作寄存在 blocking queue,始终到处理完毕才删除;如果这类申请很多的话,很容易引起内存爆掉。 解决方案抉择适合的数据结构,默认线程池关联的队列是LinkedBlockingQueue , 没有提早管制,能够应用DelayQueue DelayQueue外部应用了PriorityQueue 按工夫排序;须要本人应用Delayed 接口封装申请数据 上面是例子 测试代码,同时退出 3个须要提早10s的工作 测试后果: 合乎预期 点击关注,第一工夫理解华为云陈腐技术~

May 26, 2021 · 1 min · jiezi

关于操作系统:程序进程和线程有什么不同

本文非原创,译自https://www.backblaze.com/blo... 第一次翻译,文中有谬误的中央还请斧正,谢谢。 让咱们来说说线程想必你屡次据说过计算机程序中的“线程”了吧?然而你不能确定它是什么意思?过程又是怎么的呢?你可能晓得线程在某种程度上跟程序和过程是密切相关的,如果你不是计算机科学业余的话,兴许这就是你的了解了。 如果你是一名程序员,那么晓得这些术语的含意是相对必要的,但对于一个一般的计算机用户而言理解了它们也是有用的。有助于你应用Mac的流动监视器,Windows的工作管理器,或者Linux的Top(性能剖析工具),它能够帮忙你定位解决计算机上呈现问题的程序,或者是否须要装置更多的内存来使你的零碎运行的更好。 让咱们花几分钟来摸索计算机程序的世界,并弄请这些术语的含意。咱们将去简化并概括一些概念,涵盖的这些概念将有助于咱们理清这些术语之间的区别。 Programs(程序)首先,你可能晓得程序是存储在计算机上的旨在完特定工作的代码,程序有多种类型,其中包含计算机的函数程序和操作系统局部的程序,以及特定工作的其它程序。这些特定工作的程序也被称为“应用程序”,它包含诸如文本处理程序,web浏览程序,或通过电子邮件将音讯发送到另一台计算机的程序。 程序通常以计算机能够执行的模式存储在磁盘或者非易失性存储器中。在此之前,它们应用像C,Lisp,Pascal,或其它编程语言创立,这些编程语言应用波及逻辑,数据,设施治理,递归,以及用户交互的指令。为了在计算机中运行,最终后果是被编译成了二进制模式(1和0)的代码文本文件。另一种类型的程序被称为“解释型编程语言”,它是在运行的时候被解释成可执行代码,而不是为了运行而提前去编译。一些常见的解释型编程语言有Python,PHP,JavaScript和Ruby。 最终后果是雷同的,因为程序在运行的时候,它是以二进制的模式加载到内存中。计算机的CPU(Central Processing Unit)仅了解二进制指令,因而当CPU运行时程序须要被转换为二进制的模式。 二进制是计算机原生语言,因为电子电路有两种根本状态,关上或者敞开,它们用0和1来示意。在咱们日常应用的通用编码零碎中,是基于10进制的,每个数位都能够是从0到9的数字。在而二进制中,每个地位能够是0或1。 兴许你听过一个对于程序员的笑话,“世界上只有10种人,懂二进制的人和不懂二进制的人”十进制二进制00000100012001030011401005010160110701118100091001过程怎么工作?程序曾经以二进制的模式加载到计算机内存中了。那么接下来呢? 执行中的程序不只是须要通知计算机做什么的二进制代码。程序还须要内存和各种操作系统资源能力运行。“过程”就是咱们说的程序,它和它所须要去操作的所有资源一起被加载到内存中。“操作系统”是调配所有资源的大脑,它有多种类型,诸如macOS,IOS,Microsoft Windows,Linux,以及Android。操作系统负责管理将程序转换为运行中的过程所需的资源。 任何过程都须要一些必要的资源,如寄存器,程序计数器和栈。“寄存器”是计算机处理器(CPU)用于数据存储区域的局部。寄存器能够寄存指令,存储地址,或过程须要的其它类型的数据。“程序计数器”也被称为指令指针,它用于批示计算机在其程序序列中的地位。“栈”是一种存储计算机程序的以后子程序信息(这里集体了解为你调试代码时的执行上下文)的数据结构,被用于过程的暂存空间。它跟为过程动态分配的内存(被称为“堆”)是须要辨别开来的。 单个程序能够有多个实例,且运行中的程序的每个实例都是一个过程。每个过程有独自的内存地址空间,这意味着过程是独立运行的并且和其它过程是互相隔离的。它不能间接拜访其它过程的共享数据。从一个过程切换到另一个过程须要一些工夫(绝对地)来保留和加载寄存器,内存映射以及其它资源。 过程的这种独立是很重要的,因为操作系统会尽可能的隔离过程以保障一个过程的问题不会影响到另一个过程。你必定碰到过计算机一个应用程序卡死或者呈现问题的状况,并且你可能在不影响其它程序的状况下退出该程序。 线程如何工作?你还在跟着咱们往下浏览吗?咱们终于讲到了线程! 线程是过程的执行单元。一个过程能够有一个或者多个线程。 当一个过程开始时,它会被指定内存和资源。过程中的每个线程共享内存和资源。在单线程过程中,过程只蕴含一个线程。过程和线程是雷同的,并且只有一件事在产生。 在多线程过程中,过程蕴含多个线程,而且该过程在同时做多个事件(从技术角度而言,有时简直等于同时——想理解更多参见“并行和并发?”上面的局部) 咱们谈到了过程或线程可用的两种内存栈和堆。辨别这两类过程内存是很重要的,因为每个线程都有它本人的栈,然而过程中的所有线程将共享堆。 线程有时被称为轻量级过程,是因为它们都有本人的栈而且能够访问共享数据。因为线程跟过程以及过程中的其它线程共享雷同的地址空间(示意计算机实体所占用的内存大小),两个线程之间的通信开销就很低,这是无利的。不利的是过程中的一个线程有问题必定会影响到其它线程以及过程自身的存活。 过程 VS 线程咱们来回顾一下: 程序以编程代码的文本文件开始程序会被编译或解释成二进制的模式程序会被加载进内存程序能够运行一个或多个过程过程通常是和其它过程独立开的线程作为过程的子集存在线程之间通信比过程之间通信要容易的多线程更容易被雷同过程下的其它线程导致的问题影响线程 vs 过程 —— 劣势 和 劣势 线程过程过程操作是重量级的线程操作是更轻量的每个过程都有本人的内存空间线程应用它们所属过程的内存过程之间通信较慢,因为过程都有不同的内存地址线程之间通信比过程之间通信更快,因为对立过程的线程共享它们所属过程的内存过程之间上下文切换是代价更高的雷同过程下的线程上下文切换是代价较低的过程不与其它过程共享内存线程和同一过程下的其它线程共享内存并行和并发?你兴许有这样一个疑难,过程或者线程是否能在同时运行?答案是:这要看状况。对于多处理器或CPU内核(与古代处理器一样)的零碎,多个过程或线程能够并行的执行。对于单个处理器,它是不可能有多个线程或过程真正的同时执行。在这种状况下,CPU在运行的过程或线程之间共享,应用过程调度算法来划分CPU运行的工夫(此处不是很了解),进而产生并行执行的错觉。调配给每个工作的工夫被称为“工夫切片”。在工作之间来回切换很快,简直是无奈觉察的。术语并行(真正同时执行)和并发(过程及时的交替执行产生的同时执行的景象),用真正同时执行的操作和靠近同时执行的操作来辨别。 为什么抉择过程而不是线程,或者线程而不是过程?那么 当程序员在创立同时执行多个工作的程序时该怎么去抉择过程或线程呢?咱们在下面曾经概括了它们的差别,当初让咱们来看一个实在的例子,咱们很多人都在应用的程序——Coogle Chrome。 Google在设计Chrome浏览器的时候,它们必须思考如何同时解决许多须要计算机、通信和网络资源的不同工作。每个浏览器窗口或者选项卡与网络的多个服务通信去获取文本,程序,图像,音频,视频,以及其它资源,继而渲染这些数据以供显示和与用户交互。除此之外,浏览器能够关上多个窗口,每个窗口都有很多工作。 Google必须思考如何去解决工作的隔离。他们抉择把Chrome中每个浏览窗口作为一个独自的过程去运行,而不是像其它浏览器一样作为一个或多个线程去运行。这样做给Google带来了许多益处。将运行中的每个窗口作为一个过程爱护了总体应用程序不被渲染引擎的异样和故障影响,并限度每个渲染引擎过程对其余过程和零碎其余部分的拜访。隔离了过程中的JavaScript程序避免它占用太多的CPU工夫和内存从而导致整个浏览器失去响应 Google认真衡量了多过程的设计。每个浏览器窗口开始一个新的过程在内存和资源方面比起应用线程有更高的固定开销。然而他们敢说他们的办法最终会缩小总体的内存收缩。 当内存不够的时应用过程代替线程也能提供更好的内存用法。不沉闷的窗口会被操作系统视为较低的优先级,并且当其它过程须要内存时有资格被调换到磁盘中去。这有助于放弃用户显示的窗口更好的响应。如果窗口应用的是线程,想要齐全的分隔开已用和未应用的内存是将会更艰难,无疑节约了内存和性能。 您能够在Google的Chromium blog或Chrome Introduction Comic上理解更多的Google对Chrome的设计决策。上面的屏幕截图将展现运行在MacBook Air上关上了多个选项卡的Google Chrome过程。有些Chrome过程应用了很多的CPU工夫和资源,而有些过程应用的非常少。你能够看到每个过程也有许多线程正在运行。 零碎上的流动监视器或工作管理器在微调你的计算机或者定位解决问题时是个不错的帮手。如果你的计算机或程序运行的很慢,或者浏览器窗口在一段时间没有响应,你能够应用零碎监视器查看它的状态。有时你将会看到过程被标记为“没有响应”。试图退出那个过程而后看看你的零碎是否会运行的更快。如果某个应用程序占用着大量的内存,你应该思考抉择其它的应用程序来实现这个工作。 残余一点附录未齐全翻译,有趣味请移步原文。https://www.backblaze.com/blo...

April 22, 2021 · 1 min · jiezi

关于swoole:Swoole的进程数相关

在swoole的配置项中能够设置swoole应用的过程数,而后当咱们启动swoole服务后再服务器就能看到相干启动的过程 应用过程树示意 swoole官网对于过程的一个总结: Swoole的主过程是一个多线程的程序。其中有一组很重要的线程,称之为Reactor线程,这组线程就是真正解决TCP连贯,收发数据的线程 Swoole的主线程在接管到新的连贯后将这个连贯调配给一个固定的Reactor线程,并由这个线程负责监听此socket。在socket可读时读取数据,并进行协定解析,将申请投递到Worker过程,在socket可写时将数据发送给TCP客户端 Reactor线程数的这个参数能够调节主过程内事件处理线程的数量,能够充分利用多核。默认会启用跟CPU核数雷同的数量。倡议是设置为CPU核数的1-4倍 线程之间是无锁的,一个指令能够被并行执行。思考到操作系统调度存在肯定水平的性能损失,能够设置CPU核数为2,最大化利用多核 参考:https://segmentfault.com/q/10...

April 13, 2021 · 1 min · jiezi

关于线程:线程

过程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。一个程序至多有一个过程,一个过程至多有一个线程。

March 11, 2021 · 1 min · jiezi

关于线程:进程线程与协程傻傻分不清一文带你吃透

前言欢送来到操作系统系列,仍然采纳图解 + 大白话的模式来解说,让小白也能看懂,帮忙大家疾速科普入门 本篇开始介绍过程、线程、协程,置信很多小白们对这几个概念了解的不清晰,这里全副给你们安顿的明明白白,咱们开始进入注释吧 内容纲要 小故事小明(操作系统)开办了一家互联网小公司,因为筹备同时开发A与B两个软件,所以小明请了两个开发团队来做这件事件,别离是小王开发团队与小李开发团队,可是公司特地小,只有一个房间(C P U),而且房间(C P U)只能包容一个开发团队,为了能两个软件开发不被耽搁,小明(操作系统)决定,上午小王团队开发,下午小李团队开发(这个过程称为调度)。 小李(过程)与小王(过程)身为团队负责人,他们要操心的事件比拟多,须要对软件进行剖析整顿,做架构设计,最初再把工作细化调配给团队的每个开发人员(线程),在团队替换房间的时候,还须要把整个软件开发进度记录下来,不便下次接着开发,相比开发人员就轻松多了,每个人只负责一小块,须要记录的也只有一小块。 通过这个小故事,大伙也看进去了,一个过程治理着多个线程,就像团队负责人(过程)治理着多个开发人员(线程)一样。 过程什么是过程你关上网易云音乐会产生一个过程 ,你关上QQ会产生一个过程 ,你电脑上运行的程序都是过程 ,过程就是这么简略暴力。 当初咱们思考一个问题,有一个过程读取硬盘里的文件,这个文件特地大,须要读取很长时间,如果 C P U 始终傻傻的等硬盘返回数据,那 C P U 的利用率是非常低的。 就像烧开水,你会傻傻等水烧开吗?很显著,这段时间齐全能够去做其余的事件(比方玩玩赛博朋克2077),水烧开了再过去把水倒入水杯中,这样不香吗? C P U 也是一样,它发现 过程 在读取硬盘文件,不须要阻塞期待硬盘返回数据,间接去执行其余过程 ,当硬盘返回数据时,C P U 会收到 中断 的信号,于是 C P U 再回到之前的 过程 持续运行 这种多程序 交替执行 的形式,就是 C P U 治理多过程初步思维。 可能会有人问了, 交替执行会不会很慢,这个不必放心,因为 C P U 的执行速度与切换速度十分的快,可能就是几十或几百毫秒,超出了人类的感知,一秒钟内可能就交替运行了多个过程,所以给咱们产生 并行 的错觉,其实这叫并发。 单核 多过程交替执行 就是并发,多过程在多核运行就是并行。 过程的控制结构发明任何货色的时候,都要先无形,才有物,你造房子、造汽车或造其余货色,都要有设计图(构造),再依据设计图来发明, 过程也不例外,它也有属于本人的设计图,那就是 过程管制块(process control block,PCB),前面就简称 P C B 好了 ...

March 9, 2021 · 3 min · jiezi

关于线程:Linux内核-进程管理

1. 过程和线程1.1 定义过程是处于运行状态的程序和相干资源的总称,是资源分配的最小单位。 线程是过程的外部的一个执行序列,是CPU调度的最小单位。 有一段可执行程序代码。有一段过程专用的零碎堆栈空间和零碎空间堆栈。有过程描述符,用于形容过程的相干信息。有独立的存储空间,也就是专有的用户空间,相应的又会有用户空间堆栈。Linux零碎对于线程实现十分非凡,他并不辨别线程和过程,线程只是一种非凡的过程罢了。从下面四点因素来看,领有前三点而缺第四点因素的就是线程,如果齐全没有第四点的用户空间,那就是零碎线程,如果是共享用户空间,那就是用户线程。 1.2 次要区别过程作为分配资源的根本单位,而把线程作为独立运行和独立调度的根本单位,因为线程比过程更小,基本上不领有系统资源,故对它的调度所付出的开销就会小得多,能更高效的进步零碎多个程序间并发执行的水平。 过程和线程的次要差异在于它们是不同的操作系统资源管理形式。过程有独立的地址空间,一个过程解体后,在保护模式下不会对其它过程产生影响,而线程只是一个过程中的不同执行门路。线程有本人的堆栈和局部变量,但线程之间没有独自的地址空间,一个线程死掉就等于整个过程死掉,所以多过程的程序要比多线程的程序强壮,但在过程切换时,消耗资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用过程。 总结:linux中,过程和线程惟一区别是有没有独立的地址空间。 2. 过程描述符及工作构造32位机器上,大概有1.7KB,过程描述符残缺形容一个正在执行的过程的所有信息。 工作队列(双向循环链表) 过程描述符struct task_struct(源代码 | linnux/sched.h | v5.4) struct task_struct { volatile long state; // -1为不可运行, 0为可运行, >0为已中断 int lock_depth; // 锁的深度 unsigned int policy; // 调度策略:个别有FIFO,RR,CFS pid_t pid; // 过程标识符,用来代表一个过程 struct task_struct *parent; // 父过程 struct list_head children; // 子过程 struct list_head sibling; // 兄弟过程}2.1 调配过程描述符2.1.1 slab分配器linux采纳slab分配器调配task_struct构造 目标:对象复用和缓存着色。 slab分配器动静生成task_struct,只需在栈底(绝对于向下增长的栈)或栈顶(绝对于向上增长的栈)创立一个新构造struct thread_info。 2.1.2 过程描述符寄存PID最大值默认为32768(short int 短整形的最大值<linux/threads.h>)可通过批改/proc/sys/kernel/pid_max进步下限。 current宏查找以后正在运行过程的过程描述符。 x86零碎中,current把栈指针后13个无效位屏蔽掉,用来计算出thread_info的偏移。 ...

March 8, 2021 · 1 min · jiezi

关于线程:微服务容错时这些技术你要立刻想到

摘要:随同着微服务架构被宣传得热火朝天,一些概念也被推到了咱们背后。服务熔断、服务降级,好高大上的样子,以前可望不可即,今日终于揭开它神秘面纱。随同着微服务架构被宣传得热火朝天,一些概念也被推到了咱们背后。服务熔断、服务降级,好高大上的样子,以前可望不可即,今日终于揭开它神秘面纱。 服务雪崩效应的定义很简略,是一种因服务提供者的不可用导致服务调用者的不可用,并将不可用逐步放大的过程。 能够联合下图进行了解: 服务雪崩 上图中,A作为根底的服务提供者,为B和C提供服务,D、E、F是B和C服务的调用者,当A不可用时,将引起B和C的不可用,并将这种不可用放大到D、E、F,从而可能导致整个零碎的不可用,服务雪崩的产生可能导致分布式系统的瘫痪。 服务雪崩效应的产生个别有三个流程,服务提供者不可用 -> 重试加大流量 -> 服务调用者不可用 服务提供者不可用的呈现的起因有很多,可能是因为服务器的宕机或者网络故障,也可能是因为程序存在的Bug,也有可能是大量的申请导致服务提供者的资源受限无奈及时响应,还有可能是因为缓存击穿造成服务提供者超负荷运行等等,毕竟没有人能保障软件的齐全正确性。 在服务提供者不可用产生之后,用户可能无法忍受长时间的期待,一直地发送雷同的申请,服务调用者从新调用服务提供者,同时服务提供者中可能存在对异样的重试机制,这些都会加大对服务提供者的申请流量。然而此时的服务提供者曾经是一艘破船,它也无能无力,无奈返回无效的后果。 最初是服务调用者因为服务提供者的不能用导致了本身的解体。当服务调用者应用同步调用的时候,大量的期待线程将会耗尽线程池中的资源,最终导致服务调用者的宕机,无奈响应用户的申请,服务雪崩效应就此产生了。 断路器在分布式系统中,不同服务之间产生的调用十分常见,当服务提供者不可用时就很有可能产生服务雪崩的效应,导致整个零碎的不可用。所以为了预防这种申请的产生,能够通过断路器模式进行预防(类比电路中的断路器,在电路过大的时候主动断开,避免电线过热侵害整条电路)。 断路器模式背地的思维很简略,将近程函数调用包装到一个断路器对象中,用于监控函数调用过程的失败。一旦该函数调用的产生失败的次数在一段时间内达到肯定的阀值,那么这个断路器将会跳闸,而后接下来工夫里对该被爱护函数调用的线程将会被断路器间接返回一个谬误,而不再产生该函数的实在调用。这样子就防止了服务调用者在服务提供者不可用时发送申请,从而缩小线程池中资源的耗费,爱护了服务调用者。 断路器时序图 尽管下面的断路器在关上的时候防止了被爱护的函数调用,然而当状况恢复正常时,须要内部干涉来重置断路器,使得函数调用能够从新产生。所以正当的断路器应该具备以下的开关转化逻辑,它须要一个机制来管制它的从新闭合,图6-3中是通过一个重置工夫来决定。 断路器状态图 敞开状态: 断路器处于敞开状态,统计调用失败次数,在一段时间内达到肯定的阀值后断路器关上。关上状态: 断路器处于关上状态,对函数调用间接返回失败谬误,不产生真正的函数调用。设置了一个重置工夫窗,在重置工夫窗完结后,断路器来到半开状态。半开状态: 断路器处于半开状态,此时容许进行函数调用,当调用都胜利了(或者胜利达到肯定的比例),敞开断路器,否则认为服务没有复原,从新关上断路器。断路器的关上能保障服务调用者在调用异样服务时,疾速返回后果,防止大量的同步期待,缩小服务调用者的资源耗费。并且断路器能在关上的一段时间后持续侦测申请执行后果,提供断路器敞开的可能,复原服务的调用。 服务降级操作断路器是为了隔断服务调用者和异样服务提供者,避免了服务雪崩的景象,是一种爱护的措施。而服务降级的意思是在整体资源不够的时候,适当的放弃局部服务,将次要的资源投放到外围服务中,待渡过难关之后,再把敞开的服务重启回来。 在Hystrix中,当服务间调用产生问题时,它将采纳备用的fallback办法代替主办法执行并返回后果,这就进行了服务降级,同时触发了断路器的逻辑。当调用服务失败次数在一段时间内超过了断路器的阀值时(此时始终调用fallback中的逻辑返回后果),断路器将关上,此时将不再调用函数,而是疾速失败,间接执行fallback逻辑,服务降级,缩小服务调用者的资源耗费,爱护服务调用者中的线程资源。 资源隔离在货船中,为了避免漏水和火灾的扩散,个别会将货仓进行宰割,防止了一个货仓出事导致整艘船沉没的喜剧。同样的,在Hystrix中,也采纳了这样的舱壁模式,将零碎中的服务提供者隔离起来,一个服务提供者提早升高或者失败,并不会导致整个零碎的失败,同时也可能管制调用这些服务的并发度。 线程与线程池Hystrix中通过将调用服务线程与服务拜访的执行线程分隔开来,调用线程可能空进去去做其余的工作而不至于被服务调用的执行的阻塞过长的工夫。 在Hystrix中应用独立的线程池对应每一个服务提供者,来隔离和限度这些服务,于是,某个服务提供者的高提早或者饱和资源受限只会产生在该服务提供者对应的线程池中。如上图中,Dependency I的调用失败或者高提早仅会导致本身对应的线程池中的5个线程的阻塞,并不会影响其余服务提供者的线程池。零碎齐全与服务提供者申请隔离开来,即便服务提供者对应的线程齐全耗尽,并不会影响零碎中的其余申请。 留神在对应服务提供者的线程池被占满时,Hystrix会进入了fallback逻辑,疾速失败,爱护服务调用者的资源稳固。 信号量除了线程池外,Hystrix还能够通过信号量(计数器)来限度单个服务提供者的并发量。如果通过信号量来控制系统负载,将不再容许设置超时管制和异步化调用,这就示意在服务提供者呈现高提早,其调用线程将会被阻塞,直至服务提供者的网络申请超时,如果对服务提供者的稳定性有足够的信念,能够通过信号量来控制系统的负载。 总结咱们在这篇文章介绍了熔断、服务雪崩、服务降级等概念。在解决微服务容错时,这些都是罕用的技术,咱们须要首先理解其概念。 点击关注,第一工夫理解华为云陈腐技术~

January 28, 2021 · 1 min · jiezi

关于线程:java线程小结

线程状态 NEW顾名思义,这个状态,只存在于线程刚创立,未start之前 RUNNABLE这个状态的线程,其正在JVM中执行,然而这个"执行",不肯定是真的在运行, 也有可能是在期待CPU资源。所以,在网上,有人把这个状态辨别为READY和RUNNING两个,一个示意的start了,资源一到位随时能够执行,另一个示意真正的执行中 BLOCKED这个状态,个别是线程期待获取一个锁,来继续执行下一步的操作,比拟经典的就是synchronized关键字,这个关键字润饰的代码块或者办法,均须要获取到对应的锁,在未获取之前,其线程的状态就始终未BLOCKED,如果线程长时间处于这种状态下,咱们就是当心看是否呈现死锁的问题了。 WAITING一个线程会进入这个状态,肯定是执行了如下的一些代码,例如 Object.wait()Thread.join()LockSupport.park()当一个线程执行了Object.wait()的时候,它肯定在期待另一个线程执行Object.notify()或者Object.notifyAll()。或者一个线程thread,其在主线程中被执行了thread.join()的时候,主线程即会期待该线程执行实现。当一个线程执行了LockSupport.park()的时候,其在期待执行LockSupport.unpark(thread)。当该线程处于这种期待的时候,其状态即为WAITING。须要关注的是,这边的期待是没有工夫限度的,当发现有TIMED_WAITING这个状态和WAITING状态的区别就是,这个状态的期待是有肯定时效的,即能够了解为WAITING状态期待的工夫是永恒的,即必须等到某个条件合乎能力持续往下走,否则线程不会被唤醒。然而TIMED_WAITING,期待一段时间之后,会唤醒线程去从新获取锁。当执行如下代码的时候,对应的线程会进入到TIMED_WAITING状态 Thread.sleep(long)Object.wait(long)Thread.join(long)LockSupport.parkNanos()LockSupport.parkUntil()TERMINATED 即为线程执行完结之后的状态 创立线程的三种形式: 继承自Thread类实现Runnable接口实现Callable接口举荐应用第二种 保障高并发场景下的线程平安,能够从一下四个维度考量 数据单线程内可见。单线程总是平安的ThreadLocal 就是采纳这种形式来实现线程平安的。只读对象。线程安全类。同步与锁机制java.util.concurrent包下的分类 线程同步类并发汇合类线程治理类锁相干类参考资料:Java线程的6种状态剖析 《码出高效 Java开发手册》

September 14, 2020 · 1 min · jiezi

关于线程:线程和线程池知识和原理

August 26, 2020 · 0 min · jiezi