-
@TOC五种IO模型阻塞IO阻塞IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方式例如:A在钓鱼时,会一直盯着鱼漂,当鱼漂动时,拉动鱼竿,其余的时间都在盯着鱼漂。非阻塞IO非阻塞IO: 如果内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK错误码非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符, 这个过程称为轮询. 这对CPU来说是较大的浪费, 一般只有特定场景下才使用。例如:B也来钓鱼,B一遍玩手机一遍看着鱼漂,还时不时的问A钓没钓到鱼。B不是一直在等还可以玩手机的等。信号驱动信号驱动IO: 内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作例:此时C也来钓鱼,C将鱼竿上系了一个铃铛,C在看书,当铃铛响了C开始拉动鱼竿。C就像是信号驱动IO一样。多路转接IO多路转接: 虽然从流程图上看起来和阻塞IO类似. 实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态例:此时D拿着20根鱼竿来钓鱼,D来回看着就可以了。20根鱼竿鱼上钩的几率远大于之前的3个人。异步IO异步IO: 由内核在数据拷贝完成时, 通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据).例:E带着手下来钓鱼,E说:你在这钓,把桶钓满了拿回来给我。E然后开车走了。E根本不用在这里等待。总结:任何IO都包括2个步骤:1是等,2是拷贝读IO=等待读事件就绪+内核数据拷贝到用户空间写IO=等待写事件就绪+用户空间数据拷贝到内核空间高级IO的本质:尽可能减少等的时间比重同步和异步通信同步概念:所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回. 但是一旦调用返回,就得到返回值了; 换句话说,就是由调用者主动等待这个调用的结果异步概念:异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果; 换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果; 而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用还需注意多进程和多线程同步和异步之间的区别:进程/线程同步也是进程/线程之间直接的制约关系是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系. 尤其是在访问临界资源的时候。I/O多路转接之select系统提供select函数来实现多路复用输入/输出模型.select系统调用是用来让我们的程序监视多个文件描述符的状态变化的程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变select函数原型参数说明:参数nfds是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合;参数timeout为结构timeval,用来设置select()的等待时间参数timeout取值:NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了件;0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。fd_set结构用命令vim /usr/include/sys/select.h查看源码:select的过程:简单的select服务器将创建套接字单独封装成一个类Sock.hppselectServer.hppselect.cc下面来测试一下:在用手机测试一下:总结select就绪条件读就绪socket内核中, 接收缓冲区中的字节数, 大于等于低水位标记SO_RCVLOWAT. 此时可以无阻塞的读该文件描述符, 并且返回值大于0;socket TCP通信中, 对端关闭连接, 此时对该socket读, 则返回0;监听的socket上有新的连接请求;socket上有未处理的错误写就绪socket内核中, 发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小), 大于等于低水位标记SO_SNDLOWAT, 此时可以无阻塞的写, 并且返回值大于0socket的写操作被关闭(close或者shutdown). 对一个写操作被关闭的socket进行写操作, 会触发SIGPIPE信号;socket使用非阻塞connect连接成功或失败之后;socket上有未读取的错误;select特点可监控的文件描述符个数取决与sizeof(fd_set)的值.服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则服务器上支持的最大文件描述符是512*8=4096.将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。select缺点每次调用select, 都需要手动设置fd集合, 从接口使用角度来说也非常不便.每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大select支持的文件描述符数量太小本篇文章到这就结束了,后面还会有poll,epoll的讲解。
-
【功能模块】【操作步骤&问题现象】1、同一个Java 进程内 先调用一下es服务(开启了kerberos认证)2、再调用一下Phoenix服务【截图信息】【日志信息】(可选,上传日志内容或者附件)org.apache.zookeeper.KeeperException$SystemErrorException: KeeperErrorCode = SystemError for /hbase at org.apache.phoenix.util.ServerUtil.parseServerException(ServerUtil.java:138) at org.apache.phoenix.query.ConnectionQueryServicesImpl.ensureTableCreated(ConnectionQueryServicesImpl.java:1204) at org.apache.phoenix.query.ConnectionQueryServicesImpl.createTable(ConnectionQueryServicesImpl.java:1501) at org.apache.phoenix.query.DelegateConnectionQueryServices.createTable(DelegateConnectionQueryServices.java:119) at org.apache.phoenix.schema.MetaDataClient.createTableInternal(MetaDataClient.java:2721) at org.apache.phoenix.schema.MetaDataClient.createTable(MetaDataClient.java:1114) at org.apache.phoenix.compile.CreateTableCompiler$1.execute(CreateTableCompiler.java:192) at org.apache.phoenix.jdbc.PhoenixStatement$2.call(PhoenixStatement.java:409) at org.apache.phoenix.jdbc.PhoenixStatement$2.call(PhoenixStatement.java:392) at org.apache.phoenix.call.CallRunner.run(CallRunner.java:53) at org.apache.phoenix.jdbc.PhoenixStatement.executeMutation(PhoenixStatement.java:391) at org.apache.phoenix.jdbc.PhoenixStatement.executeMutation(PhoenixStatement.java:379) at org.apache.phoenix.jdbc.PhoenixStatement.executeUpdate(PhoenixStatement.java:1811) at org.apache.phoenix.query.ConnectionQueryServicesImpl$12.call(ConnectionQueryServicesImpl.java:2573) at org.apache.phoenix.query.ConnectionQueryServicesImpl$12.call(ConnectionQueryServicesImpl.java:2536) at org.apache.phoenix.util.PhoenixContextExecutor.call(PhoenixContextExecutor.java:76) at org.apache.phoenix.query.ConnectionQueryServicesImpl.init(ConnectionQueryServicesImpl.java:2536) at org.apache.phoenix.jdbc.PhoenixDriver.getConnectionQueryServices(PhoenixDriver.java:264) at org.apache.phoenix.jdbc.PhoenixEmbeddedDriver.createConnection(PhoenixEmbeddedDriver.java:150) at org.apache.phoenix.jdbc.PhoenixDriver.connect(PhoenixDriver.java:230) at java.sql.DriverManager.getConnection(DriverManager.java:664) at java.sql.DriverManager.getConnection(DriverManager.java:208) at com.dtwave.ai.engine.freyr.strategy.phoenix.plugin.PhoenixExecutorImpl.lambda$getConnection$0(PhoenixExecutorImpl.java:123) ... 82 common frames omittedCaused by: org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=2, exceptions:2022-03-29T03:03:45.889Z, RpcRetryingCaller{globalStartTime=1648523025126, pause=50, maxAttempts=2}, org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=2, exceptions:2022-03-29T03:03:45.197Z, RpcRetryingCaller{globalStartTime=1648523025126, pause=50, maxAttempts=2}, java.io.IOException: org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for /hbase2022-03-29T03:03:45.888Z, RpcRetryingCaller{globalStartTime=1648523025126, pause=50, maxAttempts=2}, java.io.IOException: org.apache.zookeeper.KeeperException$SystemErrorException: KeeperErrorCode = SystemError for /hbase2022-03-29T03:03:46.041Z, RpcRetryingCaller{globalStartTime=1648523025126, pause=50, maxAttempts=2}, org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=2, exceptions:2022-03-29T03:03:45.989Z, RpcRetryingCaller{globalStartTime=1648523025939, pause=50, maxAttempts=2}, java.io.IOException: org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for /hbase2022-03-29T03:03:46.040Z, RpcRetryingCaller{globalStartTime=1648523025939, pause=50, maxAttempts=2}, java.io.IOException: org.apache.zookeeper.KeeperException$SystemErrorException: KeeperErrorCode = SystemError for /hbase at org.apache.hadoop.hbase.client.RpcRetryingCallerImpl.callWithRetries(RpcRetryingCallerImpl.java:152) at org.apache.hadoop.hbase.client.HBaseAdmin.executeCallable(HBaseAdmin.java:3272) at org.apache.hadoop.hbase.client.HBaseAdmin.executeCallable(HBaseAdmin.java:3264) at org.apache.hadoop.hbase.client.HBaseAdmin.tableExists(HBaseAdmin.java:472) at org.apache.phoenix.query.ConnectionQueryServicesImpl.ensureTableCreated(ConnectionQueryServicesImpl.java:1105) ... 103 common frames omittedCaused by: org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=2, exceptions:2022-03-29T03:03:45.989Z, RpcRetryingCaller{globalStartTime=1648523025939, pause=50, maxAttempts=2}, java.io.IOException: org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for /hbase2022-03-29T03:03:46.040Z, RpcRetryingCaller{globalStartTime=1648523025939, pause=50, maxAttempts=2}, java.io.IOException: org.apache.zookeeper.KeeperException$SystemErrorException: KeeperErrorCode = SystemError for /hbase at org.apache.hadoop.hbase.client.RpcRetryingCallerImpl.callWithRetries(RpcRetryingCallerImpl.java:152) at org.apache.hadoop.hbase.client.HTable.get(HTable.java:384) at org.apache.hadoop.hbase.client.HTable.get(HTable.java:358) at org.apache.hadoop.hbase.MetaTableAccessor.getTableState(MetaTableAccessor.java:1124) at org.apache.hadoop.hbase.MetaTableAccessor.tableExists(MetaTableAccessor.java:446) at org.apache.hadoop.hbase.client.HBaseAdmin$6.rpcCall(HBaseAdmin.java:475) at org.apache.hadoop.hbase.client.HBaseAdmin$6.rpcCall(HBaseAdmin.java:472) at org.apache.hadoop.hbase.client.RpcRetryingCallable.call(RpcRetryingCallable.java:58) at org.apache.hadoop.hbase.client.RpcRetryingCallerImpl.callWithRetries(RpcRetryingCallerImpl.java:109) ... 107 common frames omittedCaused by: java.io.IOException: org.apache.zookeeper.KeeperException$SystemErrorException: KeeperErrorCode = SystemError for /hbase at org.apache.hadoop.hbase.client.ConnectionImplementation.get(ConnectionImplementation.java:2147) at org.apache.hadoop.hbase.client.ConnectionImplementation.locateMeta(ConnectionImplementation.java:820) at org.apache.hadoop.hbase.client.ConnectionImplementation.locateRegion(ConnectionImplementation.java:787) at org.apache.hadoop.hbase.client.HRegionLocator.getRegionLocation(HRegionLocator.java:64) at org.apache.hadoop.hbase.client.RegionLocator.getRegionLocation(RegionLocator.java:58) at org.apache.hadoop.hbase.client.RegionLocator.getRegionLocation(RegionLocator.java:47) at org.apache.hadoop.hbase.client.RegionServerCallable.prepare(RegionServerCallable.java:223) at org.apache.hadoop.hbase.client.RpcRetryingCallerImpl.callWithRetries(RpcRetryingCallerImpl.java:107) ... 115 common frames omittedCaused by: org.apache.zookeeper.KeeperException$SystemErrorException: KeeperErrorCode = SystemError for /hbase at org.apache.zookeeper.KeeperException.create(KeeperException.java:97) at org.apache.zookeeper.KeeperException.create(KeeperException.java:54) at org.apache.hadoop.hbase.zookeeper.ReadOnlyZKClient$ZKTask$1.exec(ReadOnlyZKClient.java:209) at org.apache.hadoop.hbase.zookeeper.ReadOnlyZKClient.run(ReadOnlyZKClient.java:356) ... 1 common frames omitted
-
下面的懒汉式是线程不安全的,支持懒加载,因为没有加锁 synchronized,所以严格意义上它并不算单例模式。样例代码:public class Singleton{ private static Singleton instance; private Singleton(){ } public static Singleton getInstance(){ if(instance == null){ return new Singleton(); } return instance; }}
-
python中的多进程模块,因为使用进程池,调用进程池和进程池的队列multiprocessing.Pool可以使用,不会出现运行失败但是使用进程池之间的队列通信multiprocessing.Manager().Queue(),为什么使用就报错?在自己的电脑运行正常这个不是python内置的吗?有谁知道原因吗?
-
【问题现象】扩容后新的节点监控不显示【问题分析】1、查看扩容节点的dms_agent进程和agent_service进程是否正常2、查看老节点的agent_service.log,日志中报错agent_service未拉起【解决方案】若无法明确哪些节点的agent_service进程未被拉起,kill掉所有agent_service进程后等待自动重新拉起gs_ssh -c "ps -ef | grep agent_service | grep -v grep | awk '{print $2}' | xargs kill -9"
-
IPC在这之前我们先简单说一下IPC,IPC是Inter-Process Communication的缩写,是进程间通信或者跨进程通信的意思,既然说到进程,大家要区分一下进程和线程,进程一般指的是一个执行单元,它拥有独立的地址空间,也就是一个应用或者一个程序。线程是CPU调度的最小单元,是进程中的一个执行部分或者说是执行体,两者之间是包含与被包含的关系。因为进程间的资源不能共享的,所以每个系统都有自己的IPC机制,Android是基于Linux内核的移动操作系统,但它并没有继承Linux的IPC机制,而是有着自己的一套IPC机制。BinderBinder就是Android中最具特色的IPC方式,AIDL其实就是通过Binder实现的,因为在我们定义好aidl文件后,studio就帮我们生成了相关的Binder类。事实上我们在使用AIDL时候继承的Stub类,就是studio帮我们生成的Binder类,所以我们可以通过查看studio生成的代码来了解Binder的工作原理。首先我们定义一个AIDL文件
-
前言在决定用这个标题之前甚是忐忑,主要是担心自己对AIDL的理解不够深入,到时候大家看了之后说——你这是什么玩意儿,就这么点东西就敢说够了?简直是坐井观天不知所谓——那样就很尴尬了。不过又转念一想,我辈年轻人自当有一种一往无前的锐气,标题大气一点岂不更好?并且大家都是文明人,总归更多的是理解与补充而不是侮辱与谩骂?所以最终还是厚颜用了这么一个不怎么有耻的标题。好了,接下来进入正题,谈谈我对AIDL的理解和认识。正文1,概述AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。是的,首先我们知道的第一点就是:AIDL是一种语言。既然是一种语言,那么相应的就很自然的衍生出了一些问题:为什么要设计出这么一门语言?它有哪些语法?我们应该如何使用它?再深入一点,我们可以思考,我们是如何通过它来达到我们的目的的?更深入一点,为什么要这么设计这门语言?会不会有更好的方式来实现我们的目的?接下来,我们就一步步的来解答上面的这些问题。ps:1,在研究AIDL相关的东西之前,一些必要的知识储备是要有的。一方面是关于Android中service相关的知识,要了解的比较通透才行,关于这方面的东西可以参考 Android中的Service:默默的奉献者 (1),Android中的Service:Binder,Messenger,AIDL(2) 这两篇博文。另一方面是关于Android中序列化的相关知识,这方面的东西文中会简单提及,但是如果想要深入的研究一下的话最好还是去找一些这方面的资料看一下。 2,我的编译环境为Android Studio2.1.2,SDK Version 23,JDK 1.7。2,为什么要设计这门语言?设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。每个进程之间都你不知我,我不知你,就像是隔江相望的两座小岛一样,都在同一个世界里,但又各自有着自己的世界。而AIDL,就是两座小岛之间沟通的桥梁。相对于它们而言,我们就好像造物主一样,我们可以通过AIDL来制定一些规则,规定它们能进行哪些交流——比如,它们可以在我们制定的规则下传输一些特定规格的数据。总之,通过这门语言,我们可以愉快的在一个进程访问另一个进程的数据,甚至调用它的一些方法,当然,只能是特定的方法。但是,如果仅仅是要进行跨进程通信的话,其实我们还有其他的一些选择,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 占用的系统资源比较多,如果是频繁的跨进程通信的话显然是不可取的;Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行,在有些要求多进程的情况下不适用——这种时候就需要使用 AIDL 了。如果想要了解它们更详细的区别的话,可以去我的另一篇博文看看:Android中的Service:Binder,Messenger,AIDL(2)3,它有哪些语法?其实AIDL这门语言非常的简单,基本上它的语法和 Java 是一样的,只是在一些细微处有些许差别——毕竟它只是被创造出来简化Android程序员工作的,太复杂不好——所以在这里我就着重的说一下它和 Java 不一样的地方。主要有下面这些点:文件类型:用AIDL书写的文件的后缀是 .aidl,而不是 .java。数据类型:AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下——在 Java 中,这种情况是不需要导包的。比如,现在我们编写了两个文件,一个叫做 Book.java ,另一个叫做 BookManager.aidl,它们都在 com.lypeer.aidldemo 包下 ,现在我们需要在 .aidl 文件里使用 Book 对象,那么我们就必须在 .aidl 文件里面写上 import com.lypeer.aidldemo.Book; 哪怕 .java 文件和 .aidl 文件就在一个包下。默认支持的数据类型包括:Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。String 类型。CharSequence类型。List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。定向tag:这是一个极易被忽略的点——这里的“被忽略”指的不是大家都不知道,而是很少人会正确的使用它。在我的理解里,定向 tag 是这样的:AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。具体的分析大家可以移步我的另一篇博文:你真的理解AIDL中的in,out,inout么?另外,Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in 。还有,请注意,请不要滥用定向 tag ,而是要根据需要选取合适的——要是不管三七二十一,全都一上来就用 inout ,等工程大了系统的开销就会大很多——因为排列整理参数的开销是很昂贵的。两种AIDL文件:在我的理解里,所有的AIDL文件大致可以分为两类。一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“Android接口定义语言”。注:所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用。
-
请问并行操作,线程数量有上限么?
-
【功能模块】gdb调试【操作步骤&问题现象】1、gdb启动调试运行时,打印警告warning: Unable to find libthread_db matching inferior's thread library, thread debugging will not be available.2、应用起来后,之前设置的断点没有生效,无法进入预设的断点gdb升级版本到11.2也不能解决这个问题【截图信息】gdb --versionGNU gdb (GDB) 11.2Copyright (C) 2022 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.gcc --versiongcc (GCC) 7.3.0Copyright (C) 2017 Free Software Foundation, Inc.本程序是自由软件;请参看源代码的版权声明。本软件没有任何担保;包括没有适销性和某一专用目的下的适用性担保。【日志信息】(可选,上传日志内容或者附件)
-
本文分享自华为云社区《[Java中提供了synchronized,为什么还要提供Lock呢?](https://bbs.huaweicloud.com/blogs/336889?utm_source=zhihu&utm_medium=bbs-ex&utm_campaign=other&utm_content=content)》,作者: 冰 河。 在Java中提供了synchronized关键字来保证只有一个线程能够访问同步代码块。既然已经提供了synchronized关键字,那为何在Java的SDK包中,还会提供Lock接口呢?这是不是重复造轮子,多此一举呢?今天,我们就一起来探讨下这个问题。 # 再造轮子? 既然JVM中提供了synchronized关键字来保证只有一个线程能够访问同步代码块,为何还要提供Lock接口呢?这是在重复造轮子吗?Java的设计者们为何要这样做呢?让我们一起带着疑问往下看。 # 为何提供Lock接口? 很多小伙伴可能会听说过,在Java 1.5版本中,synchronized的性能不如Lock,但在Java 1.6版本之后,synchronized做了很多优化,性能提升了不少。那既然synchronized关键字的性能已经提升了,那为何还要使用Lock呢? 如果我们向更深层次思考的话,就不难想到了:我们使用synchronized加锁是无法主动释放锁的,这就会涉及到死锁的问题。 # 死锁问题 如果要发生死锁,则必须存在以下四个必要条件,四者缺一不可。  - **互斥条件** 在一段时间内某资源仅为一个线程所占有。此时若有其他线程请求该资源,则请求线程只能等待。 - **不可剥夺条件** 线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放)。 - **请求与保持条件** 线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求线程被阻塞,但对自己已获得的资源保持不放。 - **循环等待条件** 在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。 # synchronized的局限性 如果我们的程序使用synchronized关键字发生了死锁时,synchronized关键是是无法破坏“不可剥夺”这个死锁的条件的。这是因为synchronized申请资源的时候, 如果申请不到, 线程直接进入阻塞状态了, 而线程进入阻塞状态, 啥都干不了, 也释放不了线程已经占有的资源。 然而,在大部分场景下,我们都是希望“不可剥夺”这个条件能够被破坏。也就是说对于“不可剥夺”这个条件,占用部分资源的线程进一步申请其他资源时, 如果申请不到, 可以主动释放它占有的资源, 这样不可剥夺这个条件就破坏掉了。 如果我们自己重新设计锁来解决synchronized的问题,我们该如何设计呢? # 解决问题 了解了synchronized的局限性之后,如果是让我们自己实现一把同步锁,我们该如何设计呢?也就是说,我们在设计锁的时候,要如何解决synchronized的局限性问题呢?这里,我觉得可以从三个方面来思考这个问题。  (1)能够响应中断。 synchronized的问题是, 持有锁A后, 如果尝试获取锁B失败, 那么线程就进入阻塞状态, 一旦发生死锁, 就没有任何机会来唤醒阻塞的线程。 但如果阻塞状态的线程能够响应中断信号, 也就是说当我们给阻塞的线程发送中断信号的时候, 能够唤醒它, 那它就有机会释放曾经持有的锁A。 这样就破坏了不可剥夺条件了。 (2)支持超时。 如果线程在一段时间之内没有获取到锁, 不是进入阻塞状态, 而是返回一个错误, 那这个线程也有机会释放曾经持有的锁。 这样也能破坏不可剥夺条件。 (3)非阻塞地获取锁。 如果尝试获取锁失败, 并不进入阻塞状态, 而是直接返回, 那这个线程也有机会释放曾经持有的锁。 这样也能破坏不可剥夺条件。 体现在Lock接口上,就是Lock接口提供的三个方法,如下所示。 // 支持中断的API void lockInterruptibly() throws InterruptedException; // 支持超时的API boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 支持非阻塞获取锁的API boolean tryLock(); - lockInterruptibly() 支持中断。 - tryLock()方法 tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。 - tryLock(long time, TimeUnit unit)方法 tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。 也就是说,对于死锁问题,Lock能够破坏不可剥夺的条件,例如,我们下面的程序代码就破坏了死锁的不可剥夺的条件。 public class TansferAccount{ private Lock thisLock = new ReentrantLock(); private Lock targetLock = new ReentrantLock(); //账户的余额 private Integer balance; //转账操作 public void transfer(TansferAccount target, Integer transferMoney){ boolean isThisLock = thisLock.tryLock(); if(isThisLock){ try{ boolean isTargetLock = targetLock.tryLock(); if(isTargetLock){ try{ if(this.balance >= transferMoney){ this.balance -= transferMoney; target.balance += transferMoney; } }finally{ targetLock.unlock } } }finally{ thisLock.unlock(); } } } } 例外,Lock下面有一个ReentrantLock,而ReentrantLock支持公平锁和非公平锁。 在使用ReentrantLock的时候, ReentrantLock中有两个构造函数, 一个是无参构造函数, 一个是传入fair参数的构造函数。 fair参数代表的是锁的公平策略, 如果传入true就表示需要构造一个公平锁, 反之则表示要构造一个非公平锁。如下代码片段所示。 //无参构造函数: 默认非公平锁 public ReentrantLock() { sync = new NonfairSync(); } //根据公平策略参数创建锁 public ReentrantLock(boolean fair){ sync = fair ? new FairSync() : new NonfairSync(); } 锁的实现在本质上都对应着一个入口等待队列, 如果一个线程没有获得锁, 就会进入等待队列, 当有线程释放锁的时候, 就需要从等待队列中唤醒一个等待的线程。 如果是公平锁, 唤醒的策略就是谁等待的时间长, 就唤醒谁, 很公平; 如果是非公平锁, 则不提供这个公平保证, 有可能等待时间短的线程反而先被唤醒。 而Lock是支持公平锁的,synchronized不支持公平锁。 最后,值得注意的是,在使用Lock加锁时,一定要在finally{}代码块中释放锁,例如,下面的代码片段所示。 try{ lock.lock(); }finally{ lock.unlock(); } 注:其他synchronized和Lock的详细说明,小伙伴们自行查阅即可。
-
WALWAL(Write-Ahead-Log)是HBase的RegionServer在处理数据插入和删除的过程中用来记录操作内容的一种日志。大致过程如下图所示,首先客户端启动一个操作来修改数据,每一个修改都封装到KeyValue对象实例中,并通过RPC调用发送到含有匹配Region的HRegionServer。一旦KeyValue到达,它们就会被发送管理相应行的HRegion实例。数据被写到WAL,然后被放入到实际拥有记录的存储文件的MemStore中。同时还会检查MemStore是否满了,如果满了就会被刷写到磁盘中去。Hlog上图表示了3个不同的region,每一个负责一段rowkey的范围。这些region将共享同一个HLog实例,从不同region来的数据写入WAL的顺序是不确定的。HLogKeyWAL使用Hadoop的SequenceFile,它将记录存储为key/values 的数据集。对于WAL,key是一个HLogKey的实例。KeyValue包括row, column family, qualifier, timestamp, value以及Key Type,Key Type可以标识一个“put”或“delete”操作。wal调用链源码分析基本调用过程如下1、首先client端先把put/delete等api操作封装成List<MutationProto>,然后使用protobuf协议使用rpc服务发送到对应的HRegionServer,HRegionServer调用execRegionServerService()方法解析发送过来的protobuf协议二进制包,通过serviceName找到相应的service并调用callMethod方法执行:2、put/delet等“写”操作会使用MultiRowMutationService这个service来作用,在service中将会调用mutateRows()方法去处理List<MutationProto>,真正调用mutateRows()的是MultiRowMutationService的一个实现类MultiRowMutationEndpoint,MultiRowMutationEndpoint类实现了hbase的行事务。从MultiRowMutationEndpoint类文档可以看出其主要作用:3、在HRegion类中mutateRowsWithLocks方法查看有没执行器(RowProcessor),如果没有则创建一个再调用processRowsWithLocks()方法。processRowsWithLocks方法是整个“写”操作最核心的方法:把写wal,刷wal以及写memstore流程都在这里流转。在这里包括异常处理一共有12步之多。原型如下:在HRegion类中重写。写WAL日志关键步骤如下:3、doWALAppend()这里HRegion会把封装好的WALEdit使用FSHLog的append方法追加到日志文件,但是由于文件本身在内存中有缓存的原因,还需要调用sync刷入磁盘。这里只是把WALEdit数据放到一个LMAX Disrutpor RingBuffer中。这个RingBuffer是一个线程安全的消息队列,在wal中主要用于有效且安全的协调多个生产者一个消费者模型。其中多个生产者就是这个append方法,将会有很多client产生数据都放到这个消息队列中,但是只有一个消费者从这个队列中取数据并调用sync方法把数据从缓存刷到磁盘,这样能保证WAL日志并发写入时日志的全局唯一顺序。在这步中会调用sync()方法,除了isMetaRegion,sync()将根据client设置的持久化等级选择是否调用wal(FSHLog)的sync方法如代码中所示当前sync_wal和fsync_wal采用的是同一策略都是:调用HFLog的sync()方法。sync()是一个阻塞方法,需要等到数据真正的刷到磁盘后,便会唤醒它,然后工作线程返回写入memstore,完成一次“写”操作。Hbase WAL 线程模型源码分析其线程模型主要实现实在FSHLog中,FSHLog是WAL接口的实现类,实现了最关键的apend()和sync()方法,其模型如图所示:这个图主要描述了HRegion中调用append和sync后,hbase的wal线程流转模型。最左边是有多个client提交到HRegion的append和sync操作。当调用append后WALEdit和WALKey会被封装成FSWALEntry类进而再封装成RingBufferTruck类放入一个线程安全的Buffer(LMAX Disruptor RingBuffer)中。当调用sync后会生成一个SyncFuture进而封装成RingBufferTruck类同样放入这个Buffer中,然后工作线程此时会被阻塞等待被notify()唤醒。在最右边会有一个且只有一个线程专门去处理这些RingBufferTruck,如果是FSWALEntry则写入hadoop sequence文件。因为文件缓存的存在,这时候很可能client数据并没有落盘。所以进一步如果是SyncFuture会被批量的放到一个线程池中,异步的批量去刷盘,刷盘成功后唤醒工作线程完成wal。1、工作线程(RingBuffer生成者)调用过程如下:FSHLog的append方法首先会从LAMX Disruptor RingbBuffer中拿到一个序号作为txid(sequence),然后把WALEdit,WALKey和sequence等构建一个FSALEntry实例entry,并把entry放到ringbuffer中。而entry以truck(RingBufferTruck,ringbuffer实际存储类型)通过sequence和ringbuffer一一对应。如果client设置的持久化等级是USER_DEFAULT,SYNC_WAL或FSYNC_WAL,那么工作线程的HRegion还将调用FSHLog的sync()方法。追踪代码可以分析出Sync()方法会往ringbuffer中放入一个SyncFuture对象,并阻塞等待完成(唤醒)。blockOnSync()方法会阻塞工作线程。2、RingBuffer消费者调用过程如下:在FSHLog中有一个私有内部类RingBufferEventHandler类实现了LAMX Disruptor的EventHandler接口,也即是实现了OnEvent方法的ringbuffer的消费者。Disruptor通过 java.util.concurrent.ExecutorService 提供的线程来触发 Consumer 的事件处理,可以看到hbase的wal中只启了一个线程,从源码注释中也可以看到RingBufferEventHandler在运行中只有单个线程。由于消费者是按照sequence的顺序刷数据,这样就能保证WAL日志并发写入时只有一个线程在真正的写入日志文件的可感知的全局唯一顺序。RingBufferEventHandler类的onEvent()(一个回调方法)是具体处理append和sync的方法。在前面说明过wal使用RingBufferTruck来封装WALEntry和SyncFuture(如下图源码),在消费线程的实际执行方法onEvent()中就是被ringbuffer通知一个个的从ringbfer取出RingBufferTruck,如果是WALEntry则使用当前HadoopSequence文件writer写入文件(此时很可能写的是文件缓存),如果是SyncFuture则简单的轮询处理放入SyncRunner线程异步去把文件缓存中数据刷到磁盘。相关代码如下:SyncRunner是一个线程,wal实际有一个SyncRunner的线程组,专门负责之前append到文件缓存的刷盘工作。SyncRunner的线程方法(run())负责具体的刷写文件缓存到磁盘的工作。首先去之前提交的synceFutues中拿到其中sequence最大的SyncFuture实例,并拿到它对应ringbuffer的sequence。再去比对当前最大的sequence,如果发现比当前最大的sequence则去调用releaseSyncFuture()方法释放synceFuture,实际就是notify通知正被阻塞的sync操作,让工作线程可以继续往下继续。SyncRunner的run方法核心调用如下:刷磁盘是很费时的操作,如果每次都同步的去刷client的回应比较快,但是写效率不高,如果异步刷文件缓存,写效率提高但是友好性降低,在考虑了写吞吐率和对client友好回应平衡后,wal选择了后者,积累了一定量(通过ringbuffer的sequence)的缓存再刷磁盘以此提高写效率和吞吐率。这个决策从hbase存储机制最初采用lsm树把随机写转换成顺序写以提高写吞吐率,可以看出是目标一致的。 这样整个wal写入也完成了。
-
以下两种方式二选一即可:自动化配置工具:cid:link_0手动配置步骤:1、以root用户登录OMS主节点切换至tmp路径下su - rootcd /tmp2、在新建目录下创建core.sh脚本,内容如下:sed -i '/^.*hard.*core.*$/d' /etc/security/limits.confsed -i '/^.*soft.*core.*$/d' /etc/security/limits.confecho "* soft core unlimited" >> /etc/security/limits.confecho "* hard core unlimited" >> /etc/security/limits.confsed -i "/fs.suid_dumpable/d" /etc/sysctl.confecho "core" > /proc/sys/kernel/core_patternsed -i '/^kernel.core_pattern.*$/d' /etc/sysctl.confecho "kernel.core_pattern=core" >> /etc/sysctl.confecho "fs.suid_dumpable = 1" >> /etc/sysctl.confecho "kernel.core_uses_pid=0" >> /etc/sysctl.conf/sbin/sysctl -p3、在新集群OMS主节点借助批量执行工具,配置集群所有节点core,用root执行如下命令即可分开命令执行core配置dos2unix /tmp/core.shcd /opt/FusionInsight_SetupTool/preinstall/tools/cluster./clusterscp.sh put /tmp/core.sh /tmp/./clustercmd.sh "sh /tmp/core.sh" 4、kill om_monitor重启集群后检查core配置是否生效gs_ssh -c "killall -u omm om_monitor"检查配置是否生效:1) ps -ef|grep masterx,找到进程号2) cd /proc/进程号。3) view limits4) cat /proc/sys/kernel/core_pattern5、 验证是否成功kill -11 从备DN进程号,查看对应实例目录下是否有core文件产生。
-
MDS想要同时运行客户端client和服务端server的进程,一个是发送数据,一个是接受数据,如何同时运行观察运行结果呢
-
现象描述Ceph集群测试读写性能,进行一轮测试后。重启集群OSD节点,继续开展读写测试,测试工具报错:java.lang.RuntimeException: Slave hd2-0 prematurely terminated.at Vdb.common.failure[common.java:335)at Vdb.SlaveStarter.startSlave(SlaveStarter.java:198)at Vdb.SlaveStarter.run(SlaveStarter.java:47)具体信息如下图所示。查看Ceph集群状态发现部分OSD状态为down,如图所示。处理步骤查看Ceph日志时发现往Ceph分配内存时失败,怀疑在OSD进程在获取内存是有异常。输入如图命令发现“osd_memory_target”的值并非官方发布的默认的4G。在ceph.conf文件中添加“osd_memory_target = 4294967296” ,使分配给每个osd的内存限制为4GB。将修改后的文件推送到其他节点。ceph-deploy --overwrite-conf admin ceph1 ceph2 ceph3 client1 client2 client3重启集群。systemctl restart ceph.target
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签