• 关于

    自编译语言是啥

    的搜索结果

回答

感觉你根本没理解什么是maven。。。我使用中根本没遇到你提到的疑问 jsp编译class是什么鬼。。没看懂,JSP不需要什么编译,这是容器的事情。。就算是你编译好了,你也脱离不了servlet容器,没多大意义。 至于你说的,发布前要把js minify,请问用mvn怎么做? 这根本就不叫事儿。maven搭配NPM和最热的vue前端框架整合在一起搞事,也不算啥事。Java开发的系统和工具,如果连这点扩展性都不到,还配叫Java么,Java还配稳坐编程语言老大的位置么。。你说的东西maven当然支持啦!,看示例配置: <!-- yuicompressor --> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>yuicompressor-maven-plugin</artifactId> <version>1.3.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>compress</goal> </goals> <configuration> <includes> <include>js/*.js</include> <include>css/*.css</include> <include>activity/**/*.css</include> </includes> <excludes> <exclude>**/*.min.js</exclude> <exclude>**/*-min.js</exclude> </excludes> <encoding>utf8</encoding> <failOnWarning>false</failOnWarning> <nosuffix>true</nosuffix> <force>true</force> <removeIncluded>true</removeIncluded> <linebreakpos>-1</linebreakpos> </configuration> </execution> </executions> </plugin> ant转maven不是分分钟的事情么,这种事情我干的不少,传统的没有任何包管理的全扔lib下的项目,200多个jar,我一天时间就能搞定。maven和某个号称最好的语言的包管理不同,maven对代码没有任何侵入性,代码一行改动都不需要,唯一需要的就是目录结构做轻微调整(甚至不调整也可以)。不存在你说的什么“重写”,真正的0侵入,反正就是不涉及任何代码修改,只需要配置下maven的POM而已。 maven本来就支持自定义目录结构啥的,只不过约定在先,你要不按你约定来也可以啊。 <build> <sourceDirectory>${basedir}/src/main/java</sourceDirectory> <scriptSourceDirectory>${basedir}/src/main/scripts</scriptSourceDirectory> <testSourceDirectory>${basedir}/src/test/java</testSourceDirectory> <outputDirectory>${basedir}/target/classes</outputDirectory> <testOutputDirectory>${basedir}/target/test-classes</testOutputDirectory> ... </build> 如果系统中有很多jar已经招不到maven坐标了,自己给它安一个坐标,上传到私服就能搞定。我在12年之前写的Java代码都是传统方式,没有任何依赖管理。12年后,老项目转maven,轻轻松松毫无障碍。######无论js minify还是jsp编译成class,我都是举例子而已,真正想问的是mvn对编译流程的支持,除了写插件还有哪些?当然你说mvn可以嵌入ant,那我现在ant一套流程都下来了,只是没有mvn那么多功能,感觉没必要换。######你刻意没谈jsp编译成class,证明你不熟,你熟悉js minify,所以给了个插件的例子。我可不可以理解成mvn自定义编译流程必须学怎么写mvn插件?没法像Ant一样把编译流程也可以xml脚本化?######先不看具体内容,补充一下,我说的重写是重写build不是项目,现在项目用ivy管理依赖,并非放到lib什么的###### 要使用Maven最好还是搭建一个Nexus   这样也是为了长远考虑 当然增加dependency的时候也容易些  jsp编译class 这个问题  我感觉你对于JSP 以及 Servlet的生命周期理解的有问题  Maven管理子模块什么的  还是很方便的吧  无非就是多加一些 ######回复 @ManderSY : 我只是拿jsp做个例子而已,而且你这样回答算是逃避问题。如果你改的不是jsp而是controller呢?重启项目发布这个问题我不太想发散开来讨论。总之就是有办法自动化实现平滑重启发布。如果你纠结jsp,那我说个别的方便你理解,发布前要把js minify,请问用mvn怎么做?######回复 @乌龟壳 : 可以接受完全重写的话 完全可以采取前后端分离来做 就没这些问题了######回复 @乌龟壳 : 我个人觉着差别还是很大的 重启服务的成本 以及风险明显比那个高很多######回复 @ManderSY : 而且我只是拿jsp举例方便理解而已。项目的模板引擎不是jsp,而且要打包js那些东西,要干的事情很多。######回复 @ManderSY : 对于一个上线要走流程的环境,预编译jsp只能说影响不大。###### maven的哲学是Convention over Configuration ,建议还是按照maven的标准来,不然还不如就继续用ant,这样的成本还低一些######它的标准要遵守可以,但始终问题要解决,预编译jsp只是我抽象出来方便大家理解的场景而已,实际要做的是真正不预编译跑不起来的功能。###### @南湖船老大 ###### @南湖船老大 我想再明确地说一下场景,不是具体的jsp编译成class什么的,比如我手头上有一个自己开发的模板引擎,我需要 第一步把模板引擎编译出来 第二步用这个编译出来的模板引擎编译*.tmpl文件成java 第三步把java编译成class打包成jar 第四部把jar放到最终的目录 我想咨询的是,类似这些需要自定义化的逻辑,mvn大概是怎样支持的,我只需要一些关键字就好了。比如mvn是否支持直接调用jar,是否可以像ant一样配置一些简单的逻辑等。###### 引用来自“乌龟壳”的评论 @南湖船老大 我想再明确地说一下场景,不是具体的jsp编译成class什么的,比如我手头上有一个自己开发的模板引擎,我需要 第一步把模板引擎编译出来 第二步用这个编译出来的模板引擎编译*.tmpl文件成java 第三步把java编译成class打包成jar 第四部把jar放到最终的目录 我想咨询的是,类似这些需要自定义化的逻辑,mvn大概是怎样支持的,我只需要一些关键字就好了。比如mvn是否支持直接调用jar,是否可以像ant一样配置一些简单的逻辑等。 当然不是问题啦,maven是Java写的,当然有插件机制啦。写个maven插件又不复杂,套路都在那了###### @南湖船老大 通过你的举例,我特意看了mvn的plugin开发的文档,发现maven对自己的定义是 "Maven" is really just a core framework for a collection of Maven Plugins. 所以我想难怪各种自定义逻辑的工具在mvn文档里找不到,原来本来就是要开发的。 看来要考虑下是否值得把Ant那套编译逻辑用mvn插件的方式重构出来,这样就能用上mvn了。 说真的Ant和Ivy太老了,很多东西实现得不好,不知道maven怎样。 谢谢。######回复 @南湖船老大 : 好的######回复 @乌龟壳 : nexus,jfrog都可以的,都有web界面,很方便管理(主要是配置权限和代理仓库地址)。一次配置后后面就不用再管了######回复 @南湖船老大 : 就是你说的私有repo啊,mvn私源,公司内项目间共享的源,不能开放出去的######回复 @乌龟壳 : 不太理解你这个需求是什么###### @南湖船老大 对了,你知不知道maven是否有只需要http/ftp和文件目录结构就能部署出来的repo,我不需要web界面去管理,手工即可。

kun坤 2020-06-08 11:18:06 0 浏览量 回答数 0

回答

感觉你根本没理解什么是maven。。。我使用中根本没遇到你提到的疑问 jsp编译class是什么鬼。。没看懂,JSP不需要什么编译,这是容器的事情。。就算是你编译好了,你也脱离不了servlet容器,没多大意义。 至于你说的,发布前要把js minify,请问用mvn怎么做? 这根本就不叫事儿。maven搭配NPM和最热的vue前端框架整合在一起搞事,也不算啥事。Java开发的系统和工具,如果连这点扩展性都不到,还配叫Java么,Java还配稳坐编程语言老大的位置么。。你说的东西maven当然支持啦!,看示例配置: <!-- yuicompressor --> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>yuicompressor-maven-plugin</artifactId> <version>1.3.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>compress</goal> </goals> <configuration> <includes> <include>js/*.js</include> <include>css/*.css</include> <include>activity/**/*.css</include> </includes> <excludes> <exclude>**/*.min.js</exclude> <exclude>**/*-min.js</exclude> </excludes> <encoding>utf8</encoding> <failOnWarning>false</failOnWarning> <nosuffix>true</nosuffix> <force>true</force> <removeIncluded>true</removeIncluded> <linebreakpos>-1</linebreakpos> </configuration> </execution> </executions> </plugin> ant转maven不是分分钟的事情么,这种事情我干的不少,传统的没有任何包管理的全扔lib下的项目,200多个jar,我一天时间就能搞定。maven和某个号称最好的语言的包管理不同,maven对代码没有任何侵入性,代码一行改动都不需要,唯一需要的就是目录结构做轻微调整(甚至不调整也可以)。不存在你说的什么“重写”,真正的0侵入,反正就是不涉及任何代码修改,只需要配置下maven的POM而已。 maven本来就支持自定义目录结构啥的,只不过约定在先,你要不按你约定来也可以啊。 <build> <sourceDirectory>${basedir}/src/main/java</sourceDirectory> <scriptSourceDirectory>${basedir}/src/main/scripts</scriptSourceDirectory> <testSourceDirectory>${basedir}/src/test/java</testSourceDirectory> <outputDirectory>${basedir}/target/classes</outputDirectory> <testOutputDirectory>${basedir}/target/test-classes</testOutputDirectory> ... </build> 如果系统中有很多jar已经招不到maven坐标了,自己给它安一个坐标,上传到私服就能搞定。我在12年之前写的Java代码都是传统方式,没有任何依赖管理。12年后,老项目转maven,轻轻松松毫无障碍。######无论js minify还是jsp编译成class,我都是举例子而已,真正想问的是mvn对编译流程的支持,除了写插件还有哪些?当然你说mvn可以嵌入ant,那我现在ant一套流程都下来了,只是没有mvn那么多功能,感觉没必要换。######你刻意没谈jsp编译成class,证明你不熟,你熟悉js minify,所以给了个插件的例子。我可不可以理解成mvn自定义编译流程必须学怎么写mvn插件?没法像Ant一样把编译流程也可以xml脚本化?######先不看具体内容,补充一下,我说的重写是重写build不是项目,现在项目用ivy管理依赖,并非放到lib什么的###### 要使用Maven最好还是搭建一个Nexus   这样也是为了长远考虑 当然增加dependency的时候也容易些  jsp编译class 这个问题  我感觉你对于JSP 以及 Servlet的生命周期理解的有问题  Maven管理子模块什么的  还是很方便的吧  无非就是多加一些 ######回复 @ManderSY : 我只是拿jsp做个例子而已,而且你这样回答算是逃避问题。如果你改的不是jsp而是controller呢?重启项目发布这个问题我不太想发散开来讨论。总之就是有办法自动化实现平滑重启发布。如果你纠结jsp,那我说个别的方便你理解,发布前要把js minify,请问用mvn怎么做?######回复 @乌龟壳 : 可以接受完全重写的话 完全可以采取前后端分离来做 就没这些问题了######回复 @乌龟壳 : 我个人觉着差别还是很大的 重启服务的成本 以及风险明显比那个高很多######回复 @ManderSY : 而且我只是拿jsp举例方便理解而已。项目的模板引擎不是jsp,而且要打包js那些东西,要干的事情很多。######回复 @ManderSY : 对于一个上线要走流程的环境,预编译jsp只能说影响不大。###### maven的哲学是Convention over Configuration ,建议还是按照maven的标准来,不然还不如就继续用ant,这样的成本还低一些######它的标准要遵守可以,但始终问题要解决,预编译jsp只是我抽象出来方便大家理解的场景而已,实际要做的是真正不预编译跑不起来的功能。###### @南湖船老大 ###### @南湖船老大 我想再明确地说一下场景,不是具体的jsp编译成class什么的,比如我手头上有一个自己开发的模板引擎,我需要 第一步把模板引擎编译出来 第二步用这个编译出来的模板引擎编译*.tmpl文件成java 第三步把java编译成class打包成jar 第四部把jar放到最终的目录 我想咨询的是,类似这些需要自定义化的逻辑,mvn大概是怎样支持的,我只需要一些关键字就好了。比如mvn是否支持直接调用jar,是否可以像ant一样配置一些简单的逻辑等。###### 引用来自“乌龟壳”的评论 @南湖船老大 我想再明确地说一下场景,不是具体的jsp编译成class什么的,比如我手头上有一个自己开发的模板引擎,我需要 第一步把模板引擎编译出来 第二步用这个编译出来的模板引擎编译*.tmpl文件成java 第三步把java编译成class打包成jar 第四部把jar放到最终的目录 我想咨询的是,类似这些需要自定义化的逻辑,mvn大概是怎样支持的,我只需要一些关键字就好了。比如mvn是否支持直接调用jar,是否可以像ant一样配置一些简单的逻辑等。 当然不是问题啦,maven是Java写的,当然有插件机制啦。写个maven插件又不复杂,套路都在那了###### @南湖船老大 通过你的举例,我特意看了mvn的plugin开发的文档,发现maven对自己的定义是 "Maven" is really just a core framework for a collection of Maven Plugins. 所以我想难怪各种自定义逻辑的工具在mvn文档里找不到,原来本来就是要开发的。 看来要考虑下是否值得把Ant那套编译逻辑用mvn插件的方式重构出来,这样就能用上mvn了。 说真的Ant和Ivy太老了,很多东西实现得不好,不知道maven怎样。 谢谢。######回复 @南湖船老大 : 好的######回复 @乌龟壳 : nexus,jfrog都可以的,都有web界面,很方便管理(主要是配置权限和代理仓库地址)。一次配置后后面就不用再管了######回复 @南湖船老大 : 就是你说的私有repo啊,mvn私源,公司内项目间共享的源,不能开放出去的######回复 @乌龟壳 : 不太理解你这个需求是什么###### @南湖船老大 对了,你知不知道maven是否有只需要http/ftp和文件目录结构就能部署出来的repo,我不需要web界面去管理,手工即可。

montos 2020-06-01 09:50:12 0 浏览量 回答数 0

回答

别逗,好么。。。 别说IDE了,就是editplus,notepad++ 这些记事本软件,甚至不用任何软件命令行照样能编译Java,而且能配置编译任何语言,但是有意义么?在没价值的事情上浪费生命,真是不知道怎么说了 ######回复 @kass : 你为啥不在我下面回复 我想把你这个当答案的 确实 尽管用了sublime 对写项目完全没帮助 sublime也就是写着爽 项目完全做不了######回复 @Koada : 普通编辑器简单的提示比如上下文出现的变量、内置类还是能提示的,参见editplus的字典。但没有ide的话java项目几乎寸步难行。。。######回复 @南湖船老大 : 那sublime怎么样? 他们还都是推荐ecip######回复 @Koada : CB仅仅是做了对代码文件图标关联和代码着色,本身不支持Java,虽然可以扩展,但没必要。比如CB就支持fortran编程,但是Java对IDE的要求太高(智能提示和依赖分析),一般的IDE做不到######那些文本编辑器确实可以自己配置成IDE 我只是好奇Code::Blocks如果不能够扩展适用java 为啥还要专门给java的文件专门配一个图标? 我感觉cb是可以支持java的 cb这个IDE很轻 我用得很顺手 而且是初学java不是去做工程 想着如果cb能拓展就不去下别的IDE了######java 编译器你都不装怎么跑? ###### @jingdor 提示信息就是我发上去的那些 我自己再琢磨琢磨######回复 @Koada : 看提示信息 要么就是设置不对了~ 。######jdk肯定是装了的###### 关键是你得把编译器连接到CB,并且配置好,不过不推荐 高亮支持这很正常啊,为什么吐槽呢??? ######其实就是好奇心吧 确实无关紧要

kun坤 2020-06-06 14:06:12 0 浏览量 回答数 0

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

回答

危险的做法,因为gets是不做边界检查的,能运行可能是越界少运气好。新的C标准已经废弃了这个函数gets()函数是遇到回车键结束的,所以即便你有定义好chara[8]l了也是没用的。为什么学c语言,都喜欢折腾这些通过stdin给入的数据,怎么就不喜欢从文件中读取块后做分析?为了交互?c语言有多少机会写gets,scanf?而且是一个个从键盘里面折腾。至于catxxx|bin这种,块读取,fread指定长度,仍然是高效和逻辑清晰的。fwrite难不难我不知道,反正scanf很简单,输入一个int一行就够。我整个程序就输入一个int,用到毛cmplex。为啥不在程序中改?不用重新编译,而且我就想交互。scanf是歪门邪道的话,世界上所有的语言都包括这个歪门邪道。有一大批人,计算机和非计算机的,学语言就是为了混论文和泡妞,别老拿自己的角度看别人。 @小郭一号新手?我现在还在带新手,fread,fwrite很难吗?没关系,我甚至给出接口,而让他学习lex的使用,做文本词汇解析,有必要知道接口吗?交互,cmp文本对比,有必要肉眼一个个判断吗?这和基础没关系,和自己的目标有关系,学点歪门邪道有屁用,学语言不是为了工程难道是为了混论文,泡妞?回复 @中山野鬼:首先谢谢你们无私的解答,我确实是一个新手,刚上大二,目前只学了C语言,这学期学校正在教授数据结构,可是我对自己的未来还是比较迷茫的,没有目标,不知道正确的努力方向,和正确的学习策略:学什么,如何学等问题。请问您我到底该如何学习,学什么,顺序又是怎么样的?如何才能成为一个真正的大神呢?回复 @小郭一号:首先谢谢你们无私的解答,我确实是一个新手,刚上大二,目前只学了C语言,这学期学校正在教授数据结构,可是我对自己的未来还是比较迷茫的,没有目标,不知道正确的努力方向,和正确的学习策略:学什么,如何学等问题。请问您我到底该如何学习,学什么,顺序又是怎么样的?如何才能成为一个真正的大神呢?回复 @中山野鬼:谈体验要有个对照。新手自己写的程序,如果没有scanf交互,跟有scanf比较,明显后者体验要好。然而输入字符的体验不如鼠标,这就是新手总想写图形界面的原因。别老拿自己的角度看别人。那要看你栈分配了多少哈,输入的东西破坏了栈会崩掉哈 首先,编译器不会报错,因为你运行之前编译器根本不知道你输入啥,你想正常输入他也报错吗?这个属于运行时问题。其次,可以为了交互而使用键盘输入,但是不要折腾,针对这个问题野鬼喷的有一定道理。最后,你这个问题分为gets和栈内存两个部分,本人能力有限无法解释清。

爱吃鱼的程序员 2020-06-14 22:04:52 0 浏览量 回答数 0

回答

分享一点点我的感受。刚毕业时,啥也不会,偶然间开始学java,跟你一样学完感觉啥也不会。接着,闷着头学javaweb、ssh框架、linux。在学习的过程中,发现自己的java基础太差,知识漏洞太多,在学新东西的过程中,不断地在回炉学习。新旧知识融合,触类旁通,慢慢的找到一点感觉。“学的东西也挺多的,有些记不住”,很多东西记不住,其实是因为不理解,我自己的经验是多搜索多看。因为很多人阐述知识点的时候,并不是站在你的知识体系上讲的,你尽量找到一个你能理解的答案。再一点,需要深入一些知识。别人都说什么jvm,我咋啥也不懂,不行,买一本书啃。看完一遍,懵b,但是建立了一点印象;再看,这一遍不是囫囵吞枣了,而是边学边查边理解。这里并不是说要你学jvm或者什么,是说要学一点深入的知识,底层或者框架原理啊,会对你理解编程语言有帮助;二是,不要畏惧困难,觉得难的,其实没那么难,无他但手熟尔。你学的越多看的越多,再学新东西往往会更快,效果更好,因为计算机里面很多东西都是相通的。操作系统、编译原理、算法,等你再看这些时,发现很多你之前学的,都在这里面呢。总之,坚持学习。

lrq1988 2019-12-02 02:59:20 0 浏览量 回答数 0

问题

尾递归 7月18日 【今日算法】

游客ih62co2qqq5ww 2020-07-27 13:19:21 3 浏览量 回答数 0

问题

C语言在 Visual Studio 2012 中编译遇到的一个诡异问题?报错

爱吃鱼的程序员 2020-06-20 19:25:05 0 浏览量 回答数 1

回答

作者:匿名用户链接:https://www.zhihu.com/question/30548640/answer/48787538来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 题主问的是调用命令,那当然是 POSIX 系 Shell 不罗嗦。如果你要个调用命令还算可以并且写起来舒服点的语言,建议找 Ruby 去。另外偏题去说 Shell 不是系统级编程语言的我也是醉了,我还真没看到过没有 Shell 能干活的 Unix/Linux 家伙的,就连你个 sysVinit 也得靠 shell 啊。并且你还触碰到了 Unix 狂魔的 G 点——Shell 用户万能的胶水能力被丢到哪里去了?随便起一个 Python Shell 抓住 stdin 灌或者 /tmp 丢个文件喂,你能干的我全都能干(这是认真的,参见 makeself 等自解压 shell 脚本形式)。讲 Google Style Guide 的算是靠了点谱(虽然还是丢下了调用命令这回事),问题是 Google 还不许用 C++ STL 呢你怎么不说?Bash 这玩意,认真写的人文件头都 source 一组小库的。嗯上面三段讲的是调用命令这个需求、最小依赖这个优点和语言本身不常被严肃使用(放着功能禁止用)这回事。接下来的内容严重不友善,涉及大量打岔,请题主手动忽略。大概大家都看得出我和生意人在微博上有些观点上的矛盾,我也承认匿名这样黑不大好。但是这和用 Shell 开命令搞管道胶水一样爽啊,我怎么会放弃?稍有常识的人都能看出,LibreWindows 冒泡排序的 shell 脚本完全不是用和 py 同一种风格写出来的。我在这里做了较为忠实的翻译:#!/bin/bashorig=(12 5 13 8 9 65) paramaters[] -> list[] bubble(){ list=("$@") local size=$((${#list[@]}-1)) i sorted=0 t until ((sorted)); do sorted=1 for ((i=0;i<size;i++)); do if ((list[i] > list[i+1])); then sorted=0; t=${list[i]} list[i]=${list[i+1]} list[i+1]=$t fi done done} bubble "${orig[@]}"echo ${list[@]}: << _NOTESReal unix-shelly way:sorted=$(for i in ${orig[@]}; do echo $i; done | sort -g)sorted=$($sorted)echo ${sorted[@]}_NOTESBash 的完整版本可以使用 declare -n 创建引用类型的变量,但是 Cygwin 上的 bash 没编译进去我也就先不用吧。提请阁下注意一点,数组本非古老的 Shell 所支持的,She-bang 不应调用 /bin/sh 而应使用 /bin/bash 执行。Bash 带着镣铐做这种拓展还要拿来说就算了,还强行丑化代码不开高亮接着便是叫人“感受一下”,大概也是先生您在微博上常用的套路了。另外我承认论现代编程 Shell 不如 Python,也承认 WoSign/CNNIC 验证就是比资本主义快。啊我忘了一点,这个问题本来就是讨论调用命令的,你跟我扯一般计算干啥?不要问我是谁,我只是个买辣条的。

xuning715 2019-12-02 01:10:00 0 浏览量 回答数 0

问题

哈,坑大了,请教个问题。。。403.10 禁止访问:配置无效 

kun坤 2020-05-27 20:05:30 7 浏览量 回答数 1

问题

10个迷惑新手的Cocoa,Objective-c开发难点和问题? 400 报错

爱吃鱼的程序员 2020-05-31 00:44:29 0 浏览量 回答数 1

回答

这么晚了,我是来帮顶的。。。。真心不会C######回复 @中山野鬼 : Microsoft认为自己就是标准。######多谢多谢。。。。。。。。。真心想不出windows为什么这么变态不按posix的标准走。。。。。######留名,php是最好的语言→_→######va_list 是个啥东西啊。######这是win的问题还是cygwin的问题啊?###### 引用来自“欣儿”的评论这是win的问题还是cygwin的问题啊? WIN下,是直接使用可变参存储的指针的,这个我以前有影响,va_list params;这个参数传入子函数出来,是不变的。。。mac和linux下,va_list是一个结构体,实际可变参存储的指针是在结构体内的,所以params传入后,子函数对可变参的调整是可以在其他函数中体现出来的。 我怀疑8成是cygwin的错误。。。 ###### 和系统有关系,MAC OS 和 Linux 是64位的,Windows 可能是 32 位的,确定是否用了64位的系统和编译器... ######回复 @ZeroOne : 32bit 应该是 4,抱歉,我看错了,windows下面是char *######就算是32位吧,那结果也不应该是8,而应该是12。。。###### 引用来自“自由之信”的评论 和系统有关系,MAC OS 和 Linux 是64位的,Windows 可能是 32 位的,确定是否用了64位的系统和编译器... 一个指针的sizeof是8个byte,难道32位有这个功能吗?哈。 sizeof(va_list) == 8 ,意思是说,他采用了  typedef char * va_list; 的方式定义,而mac 和 linux 下,显然不是这么定义。。。 ###### 鬼鬼, 是否和字节对齐方式有关系? 既然va_list是一个struct. 这里有一篇百度知道不晓得是否有用: http://zhidao.baidu.com/link?url=oCUdbozYmFqCVfyNgZ0T1_Tl0xjMHxzNszyiVHJXOgpk_XNDP50W8WipJaeBYwjW8_lClEjTcgs2srnIP9C-_aurl=oCUdbozYmFqCVfyNgZ0T1_Tl0xjMHxzNszyiVHJXOgpk_XNDP50W8WipJaeBYwjW8_lClEjTcgs2srnIP9C-_a windows是不允许重新定义 _M_ALPHA 这个编译参数的 ###### 引用来自“中山野鬼”的评论 引用来自“自由之信”的评论 和系统有关系,MAC OS 和 Linux 是64位的,Windows 可能是 32 位的,确定是否用了64位的系统和编译器... 一个指针的sizeof是8个byte,难道32位有这个功能吗?哈。 sizeof(va_list) == 8 ,意思是说,他采用了  typedef char * va_list; 的方式定义,而mac 和 linux 下,显然不是这么定义。。。 抱歉,我知道,因为两个的ABI接口定义不一样,所以定义的有所不同,不过这应该不影响其va_list的使用,如果使用方式正确的话,我觉得把你使用的这部分代码贴出来,看看会好一点 ###### 引用来自“ZeroOne”的评论 鬼鬼, 是否和字节对齐方式有关系? 既然va_list是一个struct. 这里有一篇百度知道不晓得是否有用: http://zhidao.baidu.com/link?url=oCUdbozYmFqCVfyNgZ0T1_Tl0xjMHxzNszyiVHJXOgpk_XNDP50W8WipJaeBYwjW8_lClEjTcgs2srnIP9C-_aurl=oCUdbozYmFqCVfyNgZ0T1_Tl0xjMHxzNszyiVHJXOgpk_XNDP50W8WipJaeBYwjW8_lClEjTcgs2srnIP9C-_a windows是不允许重新定义 _M_ALPHA 这个编译参数的 和字节对齐没关系哦。win下面就是 typedef char * va_list ;,我是奇怪,为什么cygwin不能按照linux下的方式处理。。。。######回复 @中山野鬼 : 恕我知道的很少,但是我看了下Cygwin里面的stdarg.h,没有的,里面有一个vararg.h,现在是空的,本来这个是Windows的定义(估计)######回复 @自由之信 : 我重复下我的问题,始终是“mac ,linux下的 va_list不是个char * ,win下是char * ,cygwin是否有正确的方式将va_list按照linux的规范来处理,而不是处理成char * ".哈######回复 @ZeroOne : 最可能是代码有问题,va_list的定义在Cygwin里面,本来就是char *,这并不影响va_list的使用######回复 @ZeroOne : 和_M_ALPHA没关系。现在也不是在谈重定义问题。哈。######没看我发给你的链接? 里面有一段话, YohanChn: 明白了,ANSI C/C++规定了6个ANSI Predefined Macros,是不能被用户定义或重定义的,而后Microsoft又补充了14个Microsoft-Specific Predefined Macros,也是不能被用户定义或重定义的。_M_ALPHA就是这14个中的一个。

kun坤 2020-06-08 11:18:47 0 浏览量 回答数 0

回答

这么晚了,我是来帮顶的。。。。真心不会C######回复 @中山野鬼 : Microsoft认为自己就是标准。######多谢多谢。。。。。。。。。真心想不出windows为什么这么变态不按posix的标准走。。。。。######留名,php是最好的语言→_→######va_list 是个啥东西啊。######这是win的问题还是cygwin的问题啊?###### 引用来自“欣儿”的评论这是win的问题还是cygwin的问题啊? WIN下,是直接使用可变参存储的指针的,这个我以前有影响,va_list params;这个参数传入子函数出来,是不变的。。。mac和linux下,va_list是一个结构体,实际可变参存储的指针是在结构体内的,所以params传入后,子函数对可变参的调整是可以在其他函数中体现出来的。 我怀疑8成是cygwin的错误。。。 ###### 和系统有关系,MAC OS 和 Linux 是64位的,Windows 可能是 32 位的,确定是否用了64位的系统和编译器... ######回复 @ZeroOne : 32bit 应该是 4,抱歉,我看错了,windows下面是char *######就算是32位吧,那结果也不应该是8,而应该是12。。。###### 引用来自“自由之信”的评论 和系统有关系,MAC OS 和 Linux 是64位的,Windows 可能是 32 位的,确定是否用了64位的系统和编译器... 一个指针的sizeof是8个byte,难道32位有这个功能吗?哈。 sizeof(va_list) == 8 ,意思是说,他采用了  typedef char * va_list; 的方式定义,而mac 和 linux 下,显然不是这么定义。。。 ###### 鬼鬼, 是否和字节对齐方式有关系? 既然va_list是一个struct. 这里有一篇百度知道不晓得是否有用: http://zhidao.baidu.com/link?url=oCUdbozYmFqCVfyNgZ0T1_Tl0xjMHxzNszyiVHJXOgpk_XNDP50W8WipJaeBYwjW8_lClEjTcgs2srnIP9C-_aurl=oCUdbozYmFqCVfyNgZ0T1_Tl0xjMHxzNszyiVHJXOgpk_XNDP50W8WipJaeBYwjW8_lClEjTcgs2srnIP9C-_a windows是不允许重新定义 _M_ALPHA 这个编译参数的 ###### 引用来自“中山野鬼”的评论 引用来自“自由之信”的评论 和系统有关系,MAC OS 和 Linux 是64位的,Windows 可能是 32 位的,确定是否用了64位的系统和编译器... 一个指针的sizeof是8个byte,难道32位有这个功能吗?哈。 sizeof(va_list) == 8 ,意思是说,他采用了  typedef char * va_list; 的方式定义,而mac 和 linux 下,显然不是这么定义。。。 抱歉,我知道,因为两个的ABI接口定义不一样,所以定义的有所不同,不过这应该不影响其va_list的使用,如果使用方式正确的话,我觉得把你使用的这部分代码贴出来,看看会好一点 ###### 引用来自“ZeroOne”的评论 鬼鬼, 是否和字节对齐方式有关系? 既然va_list是一个struct. 这里有一篇百度知道不晓得是否有用: http://zhidao.baidu.com/link?url=oCUdbozYmFqCVfyNgZ0T1_Tl0xjMHxzNszyiVHJXOgpk_XNDP50W8WipJaeBYwjW8_lClEjTcgs2srnIP9C-_aurl=oCUdbozYmFqCVfyNgZ0T1_Tl0xjMHxzNszyiVHJXOgpk_XNDP50W8WipJaeBYwjW8_lClEjTcgs2srnIP9C-_a windows是不允许重新定义 _M_ALPHA 这个编译参数的 和字节对齐没关系哦。win下面就是 typedef char * va_list ;,我是奇怪,为什么cygwin不能按照linux下的方式处理。。。。######回复 @中山野鬼 : 恕我知道的很少,但是我看了下Cygwin里面的stdarg.h,没有的,里面有一个vararg.h,现在是空的,本来这个是Windows的定义(估计)######回复 @自由之信 : 我重复下我的问题,始终是“mac ,linux下的 va_list不是个char * ,win下是char * ,cygwin是否有正确的方式将va_list按照linux的规范来处理,而不是处理成char * ".哈######回复 @ZeroOne : 最可能是代码有问题,va_list的定义在Cygwin里面,本来就是char *,这并不影响va_list的使用######回复 @ZeroOne : 和_M_ALPHA没关系。现在也不是在谈重定义问题。哈。######没看我发给你的链接? 里面有一段话, YohanChn: 明白了,ANSI C/C++规定了6个ANSI Predefined Macros,是不能被用户定义或重定义的,而后Microsoft又补充了14个Microsoft-Specific Predefined Macros,也是不能被用户定义或重定义的。_M_ALPHA就是这14个中的一个。

montos 2020-05-31 13:04:02 0 浏览量 回答数 0

回答

先说结论: 不要对接!不要对接!不要对接! 开个玩笑,以上仅代表个人观点,大家也知道这种“三体式警告”根本没有用的,我自己也研究如何对接,说不定做完后就觉得“真香”了。 为什么要对接? 首先讨论一下为什么要把 Flutter 对接到 Web 生态。 Flutter 现在是一个炙手可热的跨平台技术,能够一套代码运行在 Android、iOS、PC、IoT 以及浏览器上,被认为是下一代跨平台技术。相比于 Weex 和 React Native 可以很好地解决多平台一致性问题,原生渲染性能相近,上层没有 JS 那么厚的封装层次,整体性能会略好一些。 但是大部分兴冲冲去学 Flutter 的人疑惑的第一个问题就是:为什么 Flutter 要用 Dart?一个全新的语言意味着新的学习成本,难道 JS 不香吗?JS 不香不是还有 TypeScript 吗!事实上 Flutter 抛弃的岂止是 JS 这门语言,也抛弃了 HTML 和 CSS,设计了一套解耦得更好的 Widget 体系,Flutter 抛弃的是整个 Web,致力于打造一个新的生态,但是这个生态无法复用 Web 生态的代码和解决方案。尤其是之前所有跨平台方案 Hybrid、React Native、Weex 都是对接 Web 生态的,这让 Flutter 显得有些格格不入,也让大部分前端开发者望而却步。 下面是我整理出来的,前端开发者使用 Flutter 的各方面成本: 因为 Flutter 的开发模式和前端框架比较像(可以说就是抄的 React),所以框架的学习成本并不高,稍微高一些的是 Dart 语言的学习成本,另外还要学习如何用 Widget 组装 UI,虽然很多布局 Widget 设计得和 CSS 很像,灵活度还是差了很多。要想在真实项目中用起来,还要改造整个工具链,以“Native First”的视角做开发,开发 Flutter 和开发原生应用的链路是比较像的,和开发前端页面有较大差异。最高的还是生态成本,前端生态的积累无论是代码还是技术方案都很难复用,这是最痛的一点,生态也是 Flutter 最弱的一环。 无论是为了先进的技术理念还是出于商业私心,先不管 Flutter 为什么抛弃 Web 生态,现实问题是最大的 UI 开发者群体是前端,最丰富的生态是 Web 生态,我觉得 Web 技术也是开发 UI 最高效的方式。如果能在上层使用 Web 技术栈开发,在底层使用 Flutter 实现跨平台渲染,不是可以很好的兼顾开发效率、性能和跨平台一致性吗?还能复用 Web 技术栈大量的技术积累。 可能这些理由也不够充分,暂且先照着这个假设继续分析,最后再重新讨论到底该不该对接。 关于 Flutter 和 Web 生态的对接涉及两个方面: 从 Web 到 Flutter。就是使用 Web 技术栈来开发,然后对接到 Flutter 上实现跨平台渲染。对 Web 来说是解决性能和跨平台一致性问题,对 Flutter 来说是解决生态复用问题。从 Flutter 到 Web。就是官方已经实现的 Web support for Flutter,把已经用 Dart 开发好的 App 编译成 HTML/JS/CSS 然后运行在浏览器上,可以用于降级和外投场景。 如何实现“从 Web 到 Flutter”? 首先分析一下 Flutter 的架构图,看看可以从哪里下手。 Flutter 可以分为 Framework 和 Engine 两部分,Engine 部分比较底层也比较稳定了,最好不要动,需要改的是用 Dart 实现的 Framework。要想对接 Web 生态的话,JS 引擎肯定是要引入的,至于是否保留 Dart VM 有待讨论。图中最上面 Material 和 Cupertino 两个 UI 库前端是不需要的,前端有自己的。关键是 Widget 这部分,是替换成 HTML/CSS 的方式写 UI,还是继续保留 Widget 但是把语言换成 JS,不同方案给出的解法也不一样。 有不少方案可以实现对接,业界有挺多尝试的,我总结了下面三种方式: - TS 魔改:用 JS 引擎替换掉 Dart VM,用 JS/TS 重新实现 Flutter Framework(或者直接 dart2js 编译过来)。 - JS 对接:引入 JS 引擎同时保留 Dart VM,用前端框架对接 Flutter Framework。 - C++ 魔改:用 JS 引擎替换掉 Dart VM,用 C++ 重新实现 Flutter Framework。 TS 魔改 TS 魔改就是完全抛弃掉 Dart VM,用 TypeScript 重新实现一遍用 Dart 写的 Flutter Framework。 为啥是 TS 而不是 JS?这不是因为 TS 是个大热门嘛,而且向下兼容 JS,现在几乎所有时髦的框架都要用 TS 重写了。 这种方案的出发点是“如果能把 Flutter 的 Dart 换成 JS 就好了”,最容易想到的路就是把 Dart 翻译成 TS,或者直接用 dart2js 把代码编译成 js,但是编译出来的代码包含很多 dart:ui 之类的库的封装,生成的包也挺大的,也比较难定制需要导出的接口,不如干脆用 TS 重写一遍,工具链更熟悉一些,还可以加一些定制。 理论上讲翻译之后 Flutter 绝大部分功能都依然支持,可以复用各种 npm 包,还可以动态化,但是丧失了 AOT 能力,JS 语言的执行性能应该是不如 Dart 的。而且所有节点的布局运算都发生在 JS,底层只需要提供基础的图形能力就好了,就好像是基于 Canvas API 写了一套 UI 框架,性能未必有现存前端框架的性能高。 此外最大的问题是如何与官方 Flutter 保持一致,假如现在是从 v1.13 版本翻译过来的,以后官方升级到了 v1.15 要不要同步更新?这个过程没啥技术含量,而且需要持续投入,做起来比较恶心。 另外还需要考虑上层是用 Widget 的方式写 UI,还是用前端熟悉的 HTML+CSS。如果依然用 Widget 的话,那大部分前端组件还是用不了的,UI 还是得重写一遍。反正要重写的话,成本也没降下来,那就用 Dart 重写呗…… 直接用官方原版 Flutter 也避免每次更新都要翻译一遍 Dart 代码。所以既然选择了对接前端生态,那就要对接 CSS,不然就没有足够的价值。然而 CSS 和 Widget 的对接也是很繁琐的过程,而且存在完备性问题。 JS 对接 翻译代码的方式不够优雅,那就保留 Dart,把 JS/CSS 对接到 Widget 上面不就好了? 当然可以,这种方式是仅把 Flutter 当做了底层的渲染引擎,上层保持前端框架的写法,仅把渲染部分对接到 Flutter。现存的很多前端框架都把底层渲染能力做了抽象,可以对接到不同渲染引擎上,如 Vue/Rax 同时支持浏览器和 Weex,用同样的方式,可以再支持一个 Flutter。 这种方式对前端框架的兼容性比较好,但是链路太长了,业务代码调用前端框架接口做渲染,一顿操作之后发出了渲染指令,这个渲染指令要基于通信的方式传给 Flutter Framework,这中间涉及一次 JS 到 C++ 再到 Dart 的跨语言转换,然后再接收到渲染指令之后还要转成相应的 Widget 树,从 CSS 到 Widget 的转换依然很繁琐。而且 Widget 本身是可以带有状态的,本身就是响应式更新的,在更新时会重新生成 widget 并 diff,如果在前端更新 UI 的话,前端框架在 js 里 diff 一次 vdom,传到 Flutter 之后又 diff 一次 widget。 如果要绕过 Widget 直接对接图中的 Rendering 这一层,可以绕过 widget diff 但是得改 Flutter Framework 的渲染链路,既然要改 Flutter Framework 那为什么不直接用 TS 魔改呢,还绕过了 JS 到 Dart 的通信,又回到了第一种方案。 总结来说,这个方案的优点是:实现简单、能最大化保留前端开发体验,缺点是:渲染链路长、通信成本高、响应式逻辑冲突、CSS 转 Widget 不完备等。 C++ 魔改 想要干掉 Dart VM,就需要用其他语言重新实现用 Dart 开发的 Framework,用 JS/TS 可以,用 C++ 当然可以,最硬核的方式就是用 C++ 重新实现 Flutter 的 Framework,然后接入 JS 引擎,通过 binding 把 C++ 接口透出到 JS 环境,上层应用还是用 JS 做开发。 把 Framework 层下沉到 C++ 之后,不仅会有更好的性能,也能支持更多语言。原本 Flutter Framework 是在 Dart VM 之上的,必须依赖 Dart VM 才能运行,所以对 Dart 有强依赖;用 C++ 重新实现之后,JS 引擎是在 C++ 版 Framework 之上的,框架本身并不依赖 JS 引擎,还可以对接其他各种语言,如对接了 JVM 之后可以支持 Java 和 Kotlin,对接回 Dart VM 可以继续支持 Dart。 这个方案可以增强性能,也能保持和 Flutter 的一致性,但是改造成本和维护成本都相当高。C++ 的开发效率肯定不如 Dart,当 Flutter 快速迭代之后如何跟进是很大的问题,如果跟进不及时或者实现不一致那很可能就分化了。从 CSS 到 Widget 的转换也是不得不面对的问题。 几种方案对比 把上面几种方案画在同一张图里是这个样子的: 图中实线部分表示了跨语言的通信,太过频繁会影响性能,虚线部分表示了其他对接可能性。 从下到上,Flutter Engine 是不需要动的,这一层是跨平台的关键。Framework 则有三种语言版本,JS/TS、Dart、C++,性能是 C++ 版本最好,成本是 Dart 版本最低。然后还需要向上处理 HTML/CSS 和 Widget 的问题,可以直接对接一个前端框架,也可以直接在 C++ 层实现(不然需要透出的 binding 接口就太多了,用通信的方式也太过频繁了)。 如何实现“从 Flutter 到 Web”? 这个功能官方已经实现了,可以把使用 Dart 开发的 App 编译成 Web App 运行在浏览器上,官方文档以介绍用法和 API 为主,我这里简单分析一下内部具体的实现方案。 实现原理 结合 Flutter 的架构图来看,要实现 Web 到 Flutter 需要改造的是上层 Framework,要实现 Flutter 到 Web 需要改造的则是底层 Engine。 Framework 对 Engine 的核心依赖是 dart:ui,这是库是在 Engine 里实现的,抽象出了绘制 UI 图层的接口,底层对接 skia 的实现,向上透出 Dart 语言的接口。这样来看,对接方式就比较简单了: 使用 dart2js 把 Framework 编译成 JS 代码。基于浏览器的 API 重新实现 dart:ui,即 dart:web_ui。 把 Dart 编译成 JS 没什么问题,性能可能会有一点影响,功能都是可以完全保留的,关键是 dart:web_ui 的实现。在原生 Engine 中,dart:ui 依赖 skia 透出的 SkCanvas 实现绘制,这是一套很底层的图形接口,只定义了画线、画多边形、贴图之类的底层能力,用浏览器接口实现这一套接口还是很有挑战的。上图可以看到 Web 版 Engine 是基于 DOM 和 Canvas 实现的,底层定义了 DomCanvas 和 BitmapCanvas 两种图形接口,会把传来的 layer tree 渲染成浏览器的 Element tree,但是节点上仅包含了 position, transform, opacity 之类的样式,只用到 CSS 很小的一个子集,一些更复杂的绘制直接用 2D canvas 实现。 存在的问题 我编译了一个还算复杂的 demo 试了一下,性能很不理想,滑动不流畅,有时候图片还会闪动。生成出来的 js 代码有 1.1MB (minify 之后,未 gzip),节点层次也比较深,我评估这个页面用前端写不会超过 300KB,节点数可以少一半以上。 另外再看一下 Flutter 仓库的 issue,过滤出 platfrom-web 相关的,可以看到大量:文字编辑失效、找不到光标、ListView 在 ios 上不可滚动、checkbox/button 行为不正常、安卓滚动卡顿图片闪烁、字体失效、某些机型视频无法播放、文字选中后无法复制、无法调试…… 感觉 flutter for web 已经陷入泥潭,让人回想起前端当年处理各种浏览器兼容性的噩梦。 这些性能和兼容性问题,核心原因是浏览器未暴露足够的底层能力,以及浏览器处理手势、用户输入和方式和 Flutter 差异巨大。 实现 Flutter Engine 需要的是底层的图形接口和系统能力,虽然canvas 提供了相似的图形接口,如果全部用 canvas 实现的话很难处理可访问性、文本选择、手势、表单等问题,也会存在很多兼容性问题。所以真实方案里用的是 Canvas + DOM 混合的方式,封装层次太高了,渲染链路太长。就好像 Flutter Framework 里进行了一顿猛如虎的操作之后,节点生成好了、布局算好了、绘制属性也处理好了,就差一个画布画出来了,然后交到浏览器手里,又生成一遍 Element,再算一遍布局,在处理一遍绘制,最终才交给了底层的图形库画出来。 再比如长页面的滚动,浏览器里只要一条 CSS (overflow:scroll) 就可以让元素可滚动,手势的监听以及页面的滚动以及滚动动画都是浏览器原生实现的,不需要与 JS 交互,甚至不需要重新 layout 和 paint,只需要 compositing。如上图所示,在 Flutter 中 Animation 和 Gesture 是用 Dart 实现的,编译过来就是 JS 实现的,浏览器本身并不知道这个元素是否可滚,只是不断派发 touchmove 事件,JS 根据事件属性计算节点偏移,然后运算动画,然后把 transform 或者新的 position 作用到节点上,然后浏览器再来一遍完整的渲染流程…… 优化方案 性能和兼容性的问题还是要解决的,短期内先把 issue 解掉,长线的优化方案,官方有两种尝试: 使用 CSS Painting API 做绘制。 a, 这是还处于提案状态的新标准,可以用 JS 实现一些绘制功能,自定义 CSS 属性。 b. 目前还未实现,需要等浏览器先把 CSS Houdini 支持好。 使用 WebAssembly 版本的 Skia 做绘制 https://skia.org/user/modules/canvaskit a, 这样可以发挥 wasm 的性能优势,并且保持 skia 功能的一致。但是目前 wasm 在浏览器环境里未必有性能优势,这里不展开讨论了。 b. 已经部分实现,参考这里的配置启用功能: https://github.com/flutter/flutter/issues/41062#issuecomment-533952994 这两个方案都是想更多的利用到浏览器的底层能力,只有浏览器暴露了更多底层能力,才能更好的实现 Flutter 的 Web Engine。不过这个要等挺久的时间,我们也参与不了,现阶段想要使用 flutter for web,还是得保持现有架构,一起参与进去把 issue 解决掉,优先保障功能,其次优化性能。 一种适应性更好的架构 如果理想化一点,能不能从架构角度让 Flutter 和 Web 生态融合的更好一些呢? 回顾文章最开始的官方架构图,上面是 Framework(Dart),下面是 Engine(C++),切分在 Foundation 这一层,双方之间的交互是几何图形信息。如果还保持这个架构,把切分层次划分的更靠上一些,如下图所示,划分在 Widgets 和 Rendering 这一层,理论上讲对 Flutter 的开发者来说是无感知的,因为上层的开发语言和 Widget 接口都是不变的。 切分在这一层,Framework 和 Engine 之间的交互就不再是几何图形而是节点信息,Widget 的组合、setState 响应式更新、Widget diff 都还在 Dart 中,展开后的 RenderObject 的布局、绘制、裁剪、动画全都在 C++ 中,不仅有更好的性能,还可以与 Engine 有更好的结合。 或者说,还原本保留 Engine 的设计,把下沉的这部分逻辑上划分成 Renderer,就有了如下三层的结构: 这样划分出来的每一层都有明确的定位: Framework: 开发框架。为开发者提供可编程 API,实现响应式的开发模式,提供细粒度 Widget 供开发者自由封装和组合。Renderer: 渲染引擎。专门实现布局、绘制、动画、手势的的处理,这部分功能相对独立,是可以与开发框架解耦的,也不必与特定语言绑定。Engine: 图形引擎。实现跨平台一致的图形接口,合成输入的层并绘制到屏幕上,处理好平台力的接入和适配。 这样切分除了有性能优势以外,也使得渲染引擎摆脱了对 Dart 的依赖,能够支持多种语言,也能支持多种开发模式。对接到 Dart VM 就可以用 Dart 写代码,对接到 JS 引擎就可以用 JS 写代码,对接到 JVM 还可以写 Java,但是无论怎么写,底层的渲染能力是一样的,一套统一的布局算法,动画和手势的处理行为也是一致的。 在这样的架构下,对接 Web 生态就更容易了。Dart 和 Widget 是前端不想要的,希望能换成 JS 和 CSS,但是又想要底层的跨平台一致渲染引擎,那从 Renderer 层开始对接就好了,绕过了所有不想要的,也保留了所有想要的。 要实现 Flutter for Web 也更简单了一些。在 Engine 层做对接,一直苦于浏览器透出的底层能力不够,如果是在 Renderer 之上做对接就更容易一些,基于 JS/CSS/DOM/Canvas 的能力封装出一套 Rendering 接口,供 Widget 调用就好了,这样可以使渲染链路更短一些,但是依然要处理 Widget 和 DOM/CSS 之间的兼容性问题。 再讨论一遍:为什么要对接? 技术上已经分析完了,要想搞定 Flutter 生态和 Web 生态的对接,需要投入很大的成本,所以真正决定做之前,要先讨论清楚为什么要做对接?到底要不要做对接? 首先 Google 官方对 Flutter 的定位就是个问题。Flutter 设计之初就是不考虑 Web 生态的,甚至在刻意回避,倡导的是更贴近原生的开发方式。我之所以在开头说不要对接,原因也很简单:两种技术设计理念不同,不是朝着一个方向发展的,生态不通,技术方案不通,强行融合很可能让彼此都丧失了优势。但是业界又有很多团队在做这种尝试,说明需求是存在的,如果 Google 抵制这个方向,那就不好做了。不过现在官方已经支持了 Flutter for Web,已经向 Web 生态迈了一步,未来是否进一步与 Web 融合,也是有可能的。 另外就是跨平台技术本身的问题,浏览器发展了二三十年,已经是个很强大的跨平台产品了,几乎是 Web 的代名词了,这一点无人能敌。但是也臃肿不堪,有大量历史包袱,性能和体验不够好,和 Native 的结合度差,尤其在移动和 IoT 平台。虽然硬件性能在不断提升,但这是所有软件共享的,浏览器的性能和体验总会比 Native 差一些,差的这一些很可能就是新业务和新场景的发挥空间。观察一下近几年新诞生的业务场景,很多都是利用到了 Native 新提供的能力才火爆起来的,如 AI/AR/ 视频 / 直播 等,有因为新的 Web API 而孵化生出来的商业模式吗? 原文链接: https://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650405725&idx=1&sn=0b7476f7c7c01df7fdafda578f9ceb98&chksm=83953345b4e2ba53917ac30b709c07be15bd1c2fd5ae2a8ecfbb129b3813f771621b8fac95ca&scene=27#wechat_redirect

剑曼红尘 2020-03-10 09:54:40 0 浏览量 回答数 0

问题

【精品问答】大数据技术问题之Flink百问

问问小秘 2019-12-01 21:59:43 7280 浏览量 回答数 1

回答

尽管Python完全支持多线程编程, 但是解释器的C语言实现部分在完全并行执行时并不是线程安全的。 实际上,解释器被一个全局解释器锁保护着,它确保任何时候都只有一个Python线程执行。 GIL最大的问题就是Python的多线程程序并不能利用多核CPU的优势 (比如一个使用了多个线程的计算密集型程序只会在一个单CPU上面运行)。 在讨论普通的GIL之前,有一点要强调的是GIL只会影响到那些严重依赖CPU的程序(比如计算型的)。 如果你的程序大部分只会涉及到I/O,比如网络交互,那么使用多线程就很合适, 因为它们大部分时间都在等待。实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力,没啥可担心的。 而对于依赖CPU的程序,你需要弄清楚执行的计算的特点。 例如,优化底层算法要比使用多线程运行快得多。 类似的,由于Python是解释执行的,如果你将那些性能瓶颈代码移到一个C语言扩展模块中, 速度也会提升的很快。如果你要操作数组,那么使用NumPy这样的扩展会非常的高效。 最后,你还可以考虑下其他可选实现方案,比如PyPy,它通过一个JIT编译器来优化执行效率 (不过在写这本书的时候它还不能支持Python 3)。 还有一点要注意的是,线程不是专门用来优化性能的。 一个CPU依赖型程序可能会使用线程来管理一个图形用户界面、一个网络连接或其他服务。 这时候,GIL会产生一些问题,因为如果一个线程长期持有GIL的话会导致其他非CPU型线程一直等待。 事实上,一个写的不好的C语言扩展会导致这个问题更加严重, 尽管代码的计算部分会比之前运行的更快些。 说了这么多,现在想说的是我们有两种策略来解决GIL的缺点。 首先,如果你完全工作于Python环境中,你可以使用 multiprocessing 模块来创建一个进程池, 并像协同处理器一样的使用它。例如,假如你有如下的线程代码: # Performs a large calculation (CPU bound) def some_work(args): ... return result # A thread that calls the above function def some_thread(): while True: ... r = some_work(args) ... 修改代码,使用进程池: # Processing pool (see below for initiazation) pool = None # Performs a large calculation (CPU bound) def some_work(args): ... return result # A thread that calls the above function def some_thread(): while True: ... r = pool.apply(some_work, (args)) ... # Initiaze the pool if __name__ == '__main__': import multiprocessing pool = multiprocessing.Pool() 这个通过使用一个技巧利用进程池解决了GIL的问题。 当一个线程想要执行CPU密集型工作时,会将任务发给进程池。 然后进程池会在另外一个进程中启动一个单独的Python解释器来工作。 当线程等待结果的时候会释放GIL。 并且,由于计算任务在单独解释器中执行,那么就不会受限于GIL了。 在一个多核系统上面,你会发现这个技术可以让你很好的利用多CPU的优势。 另外一个解决GIL的策略是使用C扩展编程技术。 主要思想是将计算密集型任务转移给C,跟Python独立,在工作的时候在C代码中释放GIL。 这可以通过在C代码中插入下面这样的特殊宏来完成: #include "Python.h" ... PyObject *pyfunc(PyObject *self, PyObject *args) { ... Py_BEGIN_ALLOW_THREADS // Threaded C code ... Py_END_ALLOW_THREADS ... } 如果你使用其他工具访问C语言,比如对于Cython的ctypes库,你不需要做任何事。 例如,ctypes在调用C时会自动释放GIL。

哦哦喔 2020-04-17 17:22:08 0 浏览量 回答数 0

回答

浅谈Flutter框架原理及其生态圈 Flutter的锋芒 跨平台高性能的渲染引擎逐渐成为移动端、大前端领域的一个热点,作为其中的明星框架Flutter,经过近几年来的迅速发展,由极大的可能成为下一代跨端终端解决方案。自从2017 年 5 月,谷歌公司发布的了 Alpha 版本的 Flutter; 2018 年底 Flutter Live 发布的 1.0 版本;2019年7月发布1.5版本,截止今日(2020年2月)已经发布了v1.14.6 Beta版本。 在Flutter诞生之前,已经有许多跨平台UI框架的方案如Cordova、ReactNative、weex、uni-app、Hippy等,常见的需要处理兼容的终端平台也包括android、ios、web、Iot等,但是在大前端的浪潮下,对于企业和开发者来说开发效率和使用体验都十分重要,传统的做法莫过于分不同的团队开发不同的终端项目,如果还要继续向其他平台,拓展的话,我们需要付出的成本和时间将成倍增长。正因为如此,在这样的背景下,Flutter等跨端框架的兴起,从本质上讲,帮助开发者增加业务代码的复用率,减少因为要适配多个平台带来的工作量,从而降低开发成本、提高开发效率。 纵观已有的跨端方案,可以分为三类:Web 容器、泛 Web 容器、自绘引擎框架。 基于web容器即基于浏览器的跨平台也做得越来越好,自然管线也越来越短,与native的一些技术手段来实现性能上的相互补充。比如Egret、Cocos、Laya这些游戏引擎,它们在跨平台方面的做法多以Typescript编写,在iOS和安卓平台的各种浏览器中轻松的运行HTML5游戏,并在不同平台浏览器里提供近乎一致的用户体验,比如Egret还会提供高效的 JS-C Binding 编译机制,以满足游戏编译为原生格式的需求,不过大多数HTML游戏引擎也属于web容器这个范畴内。web容器框架也有一个明显的致命(在对体验&性能有较高要求的情况下)的缺点,那就是WebView的渲染效率和JavaScript执行性能太差。再加上Android各个系统版本和设备厂商的定制,很难保证所在所有设备上都能提供一致的体验。 泛 Web 容器框架比如ReactNative和Weex,即上层通过面向前端友好的UI,下层通过native的渲染形式,虽然同样使用类HTML+JS的UI构建逻辑,但是最终会生成对应的自定义原生控件,以充分利用原生控件相对于WebView的较高的绘制效率,同时H5与native相互补充来达到更好的用户体验,这也是一种很好的解决方案。缺陷也很明显,随着系统版本变化和API的变化,开发者可能也需要处理不同平台的差异,甚至有些特性只能在部分平台上实现,这样框架的跨平台特性就会大打折扣。 自绘引擎框架这里专指Flutter框架,从底层就承担跨端的任务和渲染方式,从目前来看,从技术的实现和方案的成熟度、产品的性能方面比较,Flutter有很大可能成为下一代主流跨平台框架。 Flutter与其他跨端框架的不同点之一就是自带渲染引擎,Flutter渲染引擎依靠跨平台的Skia图形库来实现,Skia引擎会将使用Dart语言构建的抽象的视图结构数据加工成GPU数据,交由 OpenGL 最终提供给 GPU 渲染,至此完成渲染闭环,因此可以在最大程度上保证一款应用在不同平台、不同设备上的体验一致性。 而开发语言选用的是同时支持 JIT和 AOT的 Dart语言,Dart本身提供了三种运行方式,应对web环境,用Dart2js编译成JavaScript代码,运行在常规浏览器中;使用DartVM直接在命令行中运行Dart代码;AOT方式编译成机器码,例如Flutter App框架。而且Dart 避免了抢占式调度和共享内存,可以在没有锁的情况下进行对象分配和垃圾回收,在性能方面表现相当不错,不仅保证了开发效率,代码性能和用户体验也更卓越。因此,Flutter在各类跨平台移动开发方案中脱颖而出。同时在去年2019的Google IO大会上,备受关注的Fuchsia系统虽然并没有发布,但是宣布了 Flutter除了支持开发 Android 和 iOS 程序之外,现在还支持开发Web程序了,在 I/O 大会上,谷歌发布了 Web 版 Flutter 的首个技术预览版,宣布 Flutter 将为包括 Google Home Hub 在内的 Google Smart Display 平台提供技术支持,并迈出利用 Chrome 操作系统支持桌面级应用的第一步。 很多JS开发者会思考Google Flutter团队至于为啥选择Dart而不是JS,其实Google 公司给出的原因很简单也很直接:Dart 语言开发组就在隔壁,对于 Flutter 需要的一些语言新特性,能够快速在语法层面落地实现;而如果选择了 JavaScript,就必须经过各种委员会(TC39等)和浏览器提供商漫长的决议。 Flutter绘制原理 在计算机系统中,图像的显示需要 CPU、GPU 和显示器一起配合完成:CPU 负责图像数据计算,GPU 负责图像数据渲染,而显示器则负责最终图像显示。 CPU 把计算好的、需要显示的内容交给 GPU,由 GPU 完成渲染后放入帧缓冲区,随后视频控制器根据垂直同步信号(VSync)以每秒 60 次的速度,从帧缓冲区读取帧数据交由显示器完成图像显示。 操作系统在呈现图像时遵循了这种机制,而 Flutter 作为跨平台开发框架也采用了这种底层方案。下面有一张更为详尽的示意图来解释 Flutter 的绘制原理。可以看到,Flutter 关注如何尽可能快地在两个硬件时钟的 VSync 信号之间计算并合成视图数据,然后通过 Skia 交给 GPU 渲染:UI 线程使用 Dart 来构建视图结构数据,这些数据会在 GPU 线程进行图层合成,随后交给 Skia 引擎加工成 GPU 数据,而这些数据会通过 OpenGL 最终提供给 GPU 渲染。 Skia原理 Skia 是一款用由C++ 开发的2D 图像绘制引擎。在2005 年被 Google 公司收购后被广泛应用在 Android和其他等核心产品上,Skia 目前是Android 官方的图像渲染引擎,因此 Flutter Android SDK 无需内嵌 Skia 引擎就可以获得天然的 Skia 支持;而对于 iOS 平台来说,由于 Skia 是跨平台的,因此它作为 Flutter iOS 渲染引擎被嵌入到 Flutter 的 iOS SDK 中,替代了 iOS 闭源的 Core Graphics/Core Animation/Core Text,这也正是 Flutter iOS SDK 打包的 App 包体积比 Android 要大一些的原因。 底层渲染能力统一了,上层开发接口和功能体验也就随即统一了,开发者再也不用操心平台相关的渲染特性了。也就是说,Skia 保证了同一套代码调用在 Android 和 iOS 平台上的渲染效果是完全一致的。 Flutter架构 Framework底层是Flutter引擎,引擎主要负责图形绘制(Skia)、文字排版(libtxt)和提供Dart运行时,引擎全部使用C++实现,Framework层使我们可以用Dart语言调用引擎的强大能力。Flutter 架构采用分层设计,从下到上分为三层,依次为:Embedder、Engine、Framework。 Embedder 是操作系统适配层,实现了渲染 Surface 设置,线程设置,以及平台插件等平台相关特性的适配。从这里我们可以看到,Flutter 平台相关特性并不多,这就使得从框架层面保持跨端一致性的成本相对较低。 Engine 层主要包含 Skia、Dart 和 Text,实现了 Flutter 的渲染引擎、文字排版、事件处理和 Dart 运行时等功能。Skia 和 Text 为上层接口提供了调用底层渲染和排版的能力,Dart 则为 Flutter 提供了运行时调用 Dart 和渲染引擎的能力。而 Engine 层的作用,则是将它们组合起来,从它们生成的数据中实现视图渲染。 Framework 层则是一个用 Dart 实现的 UI SDK,包含了动画、图形绘制和手势识别等功能。为了在绘制控件等固定样式的图形时提供更直观、更方便的接口,Flutter 还基于这些基础能力,根据 Material 和 Cupertino 两种视觉设计风格封装了一套 UI 组件库,开发者可以直接使用这些组件库。 Flutter运行流程 页面中的各界面元素(Widget)以树的形式组织,即控件树。Flutter 通过控件树中的每个控件创建不同类型的渲染对象,组成渲染对象树。在Flutter界面渲染过程分为三个阶段:布局、绘制、合成,布局和绘制在Flutter框架中完成,合成则交由引擎负责。 Flutter 采用深度优先机制遍历渲染对象树,决定渲染对象树中各渲染对象在屏幕上的位置和尺寸。在布局过程中,渲染对象树中的每个渲染对象都会接收父对象的布局约束参数,决定自己的大小,然后父对象按照控件逻辑决定各个子对象的位置,最终完成布局过程。这里只需要注意一点,无论布局还是绘制,都是父子间的遍历关系:父Widget的布局需要依赖子Widget的布局结果;而绘制则反过来(子Widget需要盖在父Widget上),布局是后续遍历,绘制是前序遍历,他们都是深度优先遍历。 Flutter生命周期 可以看到,Flutter中State 的生命周期可以分为 3 个阶段:创建(插入视图树)、更新(在视图树中存在)、销毁(从视图树中移除)。接下来,我们一起看看每一个阶段的具体流程。 第一步创建 State 初始化时会依次执行 :构造方法 -> initState -> didChangeDependencies -> build,随后完成页面渲染。构造方法是 State 生命周期的起点,Flutter 会通过调用StatefulWidget.createState() 来创建一个 State。我们可以通过构造方法,来接收父 Widget 传递的初始化 UI 配置数据。这些配置数据,决定了 Widget 最初的呈现效果。 initState,会在 State 对象被插入视图树的时候调用。这个函数在 State 的生命周期中只会被调用一次,所以我们可以在这里做一些初始化工作,比如为状态变量设定默认值。 didChangeDependencies 则用来专门处理 State 对象依赖关系变化,会在 initState() 调用结束后,被 Flutter 调用。 build,作用是构建视图。经过以上步骤,Framework 认为 State 已经准备好了,于是调用 build。我们需要在这个函数中,根据父 Widget 传递过来的初始化配置数据,以及 State 的当前状态,创建一个 Widget 然后返回。 第二步更新 Widget 的状态更新,主要由个方法触发:setState、didchangeDependencies、didUpdateWidget。 setState:我们最熟悉的方法之一。当状态数据发生变化时,我们总是通过调用这个方法告诉 Flutter:“我这儿的数据变啦,请使用更新后的数据重建 UI!” didChangeDependencies:State 对象的依赖关系发生变化后,Flutter 会回调这个方法,随后触发组件构建。哪些情况下 State 对象的依赖关系会发生变化呢?典型的场景是,系统语言 Locale 或应用主题改变时,系统会通知 State 执行 didChangeDependencies 回调方法。 didUpdateWidget:当 Widget 的配置发生变化时,比如,父 Widget 触发重建(即父 Widget 的状态发生变化时),热重载时,系统会调用这个函数。一旦这三个方法被调用,Flutter 随后就会销毁老 Widget,并调用 build 方法重建 Widget。 第三步销毁 比如组件被移除,或是页面销毁的时候,系统会调用 deactivate 和 dispose 这两个方法,来移除或销毁组件。 Flutter生态圈及其常用框架 一项技术一个框架是否流行,最直观的体现就是它的生态圈是否活跃,下面列举了一些Flutter开发中常用的库工具。 参考文献 1、[Flutter原理与实践](https://tech.meituan.com/2018/08/09/waimai-flutter-practice.html) 少杰 2、[Flutter框架技术概览](https://flutter.dev/docs/resources/technical-overview) 3、[Flutter中文官网](https://pub.dartlang.org/flutter/) 4、[Flutter插件仓库](https://pub.dev/flutter/packages)

罗思雨 2020-02-27 11:47:50 0 浏览量 回答数 0

回答

JavaScript (ECMAScript) :JavaScript 是脚本语言。JavaScript和ECMAScript通常被人用来表达相同的含义,但是JavaScript并不是这么一点含义,它是由ECMAScript 核心. DOM 文档对象模型. BOM 浏览器对象模型 这三部分组成。浏览器会在读取代码时,逐行地执行脚本代码。而对于传统编程来说,会在执行前对所有代码进行编译。 组成部分包括语法,类型,语句,关键字,保留字,操作符,对象。 其中,文档对象模型(DOM , Document Object Model)是针对XML但是经过拓展用于HTML的应用程序编程接口。DOM把整个页面映射为一个多层节点结构,开发人员借助DOM Api对节点进行操作。可以通过浏览器F12进入开发者模式,查看层级关系。当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。HTML DOM 模型被构造为对象的树。通过可编程的对象模型,JavaScript 获得了足够的能力来创建动态的 HTML。 功能大致上: · JavaScript 能够改变页面中的所有 HTML 元素 · JavaScript 能够改变页面中的所有 HTML 属性 · JavaScript 能够改变页面中的所有 CSS 样式 · JavaScript 能够对页面中的所有事件做出反应 同时,浏览器对象模型(Browser Object Model)使用BOM控制浏览器显示页面意外的部分。 javaScript脚本加载方式 1 通过在网页中加入标记JavaScript的开始和结束,将JavaScript代码放到之间 2 也可以引入一个外部的JavaScript文件,这个JavaScript文件一般以.js作为扩展名 3 原则上,放在之间。但视情况可以放在网页的任何部分 4 一个页面可以有几个,不同部分的方法和变量,可以共享。 javaScript语句开发 (1)对大小写敏感 (2)自动忽略多余的空格 (3)在文本字符串中使用反斜杠对代码行进行换行 (4)单行注释(//)多行注释(/* */) JavaScript 是一个程序语言。语法规则定义了语言结构。 JavaScript 字面量 在编程语言中,一般固定值称为字面量,如 3.14。 数字(Number)字面量 可以是整数或者是小数,或者是科学计数(e)。 字符串(String)字面量 可以使用单引号或双引号: 数组(Array)字面量 定义一个数组: [40, 100, 1, 5, 25, 10] 对象(Object)字面量 定义一个对象: {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"} 函数(Function)字面量 定义一个函数: function myFunction(a, b) { return a * b;} **JavaScript 变量 ** 在编程语言中,变量用于存储数据值。 JavaScript 使用关键字 var 来定义变量, 使用等号来为变量赋值: var x, length x = 5 length = 6 JavaScript 操作符 JavaScript使用 算术运算符 来计算值: (5 + 6) * 10 JavaScript使用赋值运算符给变量赋值: x = 5 y = 6 z = (x + y) * 10 JavaScript语言有多种类型的运算符: JavaScript 语句 在 HTML 中,JavaScript 语句向浏览器发出的命令。 语句是用分号分隔: x = 5 + 6; y = x * 10; JavaScript 关键字 JavaScript 关键字用于标识要执行的操作。 和其他任何编程语言一样,JavaScript 保留了一些关键字为自己所用。 var 关键字告诉浏览器创建一个新的变量: var x = 5 + 6; var y = x * 10; JavaScript 同样保留了一些关键字,这些关键字在当前的语言版本中并没有使用,但在以后 JavaScript 扩展中会用到。 以下是 JavaScript 中最重要的保留字(按字母顺序): JavaScript 注释 不是所有的 JavaScript 语句都是"命令"。双斜杠 // 后的内容将会被浏览器忽略: // 我不会执行 JavaScript 数据类型 JavaScript 有多种数据类型:数字,字符串,数组,对象等等: var length = 16; // Number 通过数字字面量赋值 var points = x * 10; // Number 通过表达式字面量赋值 var lastName = "Johnson"; // String 通过字符串字面量赋值 var cars = ["Saab", "Volvo", "BMW"]; // Array 通过数组字面量赋值 var person = {firstName:"John", lastName:"Doe"}; // Object 通过对象字面量赋值 数据类型的概念 编程语言中,数据类型是一个非常重要的内容。 为了可以操作变量,了解数据类型的概念非常重要。 如果没有使用数据类型,以下实例将无法执行: 16 + "Volvo" 16 加上 "Volvo" 是如何计算呢? 以上会产生一个错误还是输出以下结果呢? "16Volvo" 你可以在浏览器尝试执行以上代码查看效果。 在接下来的章节中你将学到更多关于数据类型的知识。 JavaScript 函数 JavaScript 语句可以写在函数内,函数可以重复引用: 引用一个函数 = 调用函数(执行函数内的语句)。 function myFunction(a, b) { return a * b; // 返回 a 乘以 b 的结果 } JavaScript 字母大小写 JavaScript 对大小写是敏感的。 当编写 JavaScript 语句时,请留意是否关闭大小写切换键。 函数 getElementById 与 getElementbyID 是不同的。 同样,变量 myVariable 与 MyVariable 也是不同的。 JavaScript 字符集 JavaScript 使用 Unicode 字符集。 Unicode 覆盖了所有的字符,包含标点等字符。 三 推荐学习网站 JS具体的语法内容还有很多,可以参考官方API或者学习网站完成掌握,简单易学,推荐网站 菜鸟教程:https://www.runoob.com/js/js-tutorial.html w3cschool:https://www.w3school.com.cn/js/index.asp 四 推荐学习书籍 引用自 https://www.cnblogs.com/xhqq/p/7561384.html 个人觉得不错的,没事可以翻翻的。书籍如下: 《javascript设计模式》,张容铭写的,可能不太适合零基础的,是非常不错的进阶书籍。 《javascript面向对象编程指南》,风格轻松易懂,比较适合初学者,原型那块儿讲得透彻,12种继承方式呢。 《js权威指南》、《js高级程序设计》,这两本书经典是经典,但是太厚,适合把其中任意一章都当成一本书来读。洋洋洒洒,很难一口气看完。比较适合当做参考书。 《你不知道的javascript》狙击js核心细节,闭包、原型、this讲得都还清楚。 《js设计模式与开发实践》js设计模式也是要学的,此书把js的设计模式讲得非常清晰,一点不晦涩,看起来没多少难度。 《正则指引》,分析源码时,如果正则表达式不懂,没法进行下去的。此书相对来说讲得比较清晰。 《基于MVC的JavaScript Web富应用开发》,看完后,基本能写出自己的mvc框架了。是本好书。 《javascript函数式编程》,js是一门函数式语言,此书是函数式编程一个入门,函数是一等公民那是非常重要的。 《js忍者秘籍》,jq作者写的,没有传说中的那么难读,话说就算你看完并理解所有知识点,也不会达到世界高手级别的。因为你还没有做到随心所欲。 《javascript框架设计》,如果初看此书,会觉得此书有罗列代码之嫌。在我看来,此书讲究的是框架的全局观。以上书籍是我认为是成就高手之路上必须看的,也需要反复看。 css相关的书籍,说实话我看得比较少,总共有六七本吧。有两本必须推荐一下: 《css权威指南》,css基础知识点那是讲得非常清楚的。什么层叠优先级、line-height啥的。不是随便一本书都敢叫“权威指南”的。 《css揭秘》,此书我也是不断的看,此书才不屑于全面讲css3各属性呢。css规范文档能讲的,它只会讲你最不在意的。此书解决的47问题,解决思路和解决方案同等重要,很有启发性。以上各书你都可以不买,至少买本此书吧

问问小秘 2020-03-03 09:32:57 0 浏览量 回答数 0

问题

Java技术1000问(3)【精品问答】

问问小秘 2020-06-02 14:27:10 42 浏览量 回答数 1

问题

第6篇 指针数组字符串(下):报错

kun坤 2020-06-08 11:01:44 4 浏览量 回答数 1

回答

怎么 没人来呀 @中山野鬼###### 1、如果想去掉while(true),可以考虑通知实现; 2、关于自动重连的问题,可以考虑重发送逻辑中抽离出来,采用心跳检测完成; 3、另外发送速率统计部分也应该抽离出来。 4、上多通道要考虑资源使用可控。 5、实在不行按照业务拆分成多模块,用redis 或mq类的扩展一下架构设计; ######回复 @OS小小小 : map =(Map)JSONObject.parse(SendMsgCMPP2ThredPoolByDB.ZhangYi.take()); 换成take,阻塞线程,试试。######回复 @OS小小小 : 1、通知只是告知队列里有新的数据需要处理了; 5、内存队列换成redis队列 实现成本增加,但是可扩展性增加;######1、通知实现的话 ,岂不是 无法保证 最少发送么,又会陷入另一个问题中 是吗? 或者是我的想法不对么? 2、嗯,这一块可以这样做。谢谢你 3、速率统计这里 我目前想不到怎么抽离、既可以控制到位,又可以保证不影响。。。 5、redis 是有的 但是 redis的队列的话 跟我这个 没啥区别吧,可能速度更快一点。######while(true) 里面 没数据最起码要休眠啊,不停死循环操作,又没有休眠cpu不高才怪######回复 @OS小小小 : 休眠是必须的,只是前面有数据进来,可以用wait notify 的思路通知,思路就是这样,CountDownLatch 之类多线程通讯也可以实现有数据来就能立即处理的功能######嗯,目前在测试 排除没有数据的情况,所以这一块没有去让他休眠,后面会加进去。 就针对于目前这种情况,有啥好办法吗###### 我的思路是:一个主线程,多个任务子线程。 主线程有一层while(true),这个循环是不断的扫描LinkedBlockingQueue是否有数据,有则交个任务子线程(也就是你这里定义的线程池)处理,而不是像你这样每个子任务线程都有一个while(true) ######这才是对的做法######嗯,这思路可以。谢谢哈###### 引用来自“K袁”的评论 我的思路是:一个主线程,多个任务子线程。 主线程有一层while(true),这个循环是不断的扫描LinkedBlockingQueue是否有数据,有则交个任务子线程(也就是你这里定义的线程池)处理,而不是像你这样每个子任务线程都有一个while(true) 正确做法. 还有就是 LinkedBlockingQueue 本身阻塞的,while(true)没问题,主要在于不需要每个发送线程都去block######while(true)不加休眠就会这样###### java 的线程数量大致要和cpu数量一致,并不是越多越快,线程调度是很消耗时间的。要用好多线程,就需要设计出好的多线程业务模型,不恰当的sleep和block是性能的噩梦。利用好LinkedBlockingQueue,队列空闲时读队列的线程会释放cpu。利用消息触发后续线程工作,就没必要使用while(true)来不停的扫描。 ######@蓝水晶飞机 看到你要比牛逼,我就没有兴趣跟你说话了######回复 @不日小鸡 : 我就是装逼怎么啦,特么的装逼装出样子来的,起码也比你牛逼啊。######回复 @蓝水晶飞机 : 你说这话不能掩盖你没有回复我的问题又来回复我导致装逼失败的事实。 那你不是楼主你回复我干什么,还不是回答我的问题。 不要装逼了好么,装多就成傻逼了######回复 @不日小鸡 : 此贴楼主不是你,装什么逼。######回复 @王斌_ : 这些我都知道,我的意思是你这样回复可能会误导其他看帖子的人或者新手,让他们以为线程数就等于CPU数###### 引用来自“OS小小小”的评论 怎么 没人来呀 @中山野鬼 抬举我了。c++ 我还敢对不知深浅的人说,“权当我不懂”,java真心只是学过,没有实际工程上的经验。哈。而且我是c的思维,面对c适合的应用开发,是反对使用线程的。基本思维是,执行模块的生命周期不以任务为决定,同类的执行模块,可根据物理硬核数量,形成对应独立多个进程,但绝对不会同类的任务独立对应多个线程。哈。所以java这类面向线程的设计,没办法参与讨论。设计应用目标不同,系统组织策略自然有异。 唯一的建议是:永远不要依赖工具,特别是所谓的垃圾资源处理回收机制,无论它做的再好,一旦你依赖,必然你的代码,在不久的将来会因为系统设计规模的变大,而变的垃圾。哈。 听不懂的随便喷,希望听懂的,能记得这个观点,这不是我一个人的观点。 ######给100万像素做插值运算进行染色特效,请问单线程怎么做比多线程快?###### @乌龟壳 : 几种方法都可以,第一是按照计算步骤,每个进程处理一个步骤,然后切换共享空间(这没有数据传递逻辑上的额外开销),就是流水思维。第二个是block的思维,同样的几个进程负责相同计算,但负责不同片区。同时存在另一类的进程是对前期并发处理完的工作进行边界处理。 你这个例子体现不出进程和线程的差异的。 如果非要考虑进程和线程在片内cache的差异,如果没记错(错了大家纠正哈),进程之间的共享是在二级缓存之间吧。即便线程能做到一级缓存之间的共享,但对于这种大批量像素的计算,用进程仍然是使用 dma,将数据成块载入一级缓存区域进行处理,而这个载入工作和计算工作是同步的。不会有额外太多的延迟。 你举的这个例子,还真好是我以前的老本行。再说了。像素计算,如今都用专用计算处理器了吧。还用x86或arm来处理,不累死啊。哈。 而且这种东西java不适合,同样的处理器,用c写,基本可以比java快1到2倍。因为c可以直接根据硬件特性和计算逻辑特点有效调度底层硬件驱动方式。而java即便你用了底层优化的官方库,仍然不能保证硬件与计算目标特性的高度整合。 ######回复 @中山野鬼 : 简单来说,你的多个进程处理结果进行汇总的时候,是不是要做内存复制操作?如果是多线程天然就不用,多进程用系统的共享内存机制也不用,问题是既然用了共享内存,和多线程就没区别了。######回复 @乌龟壳 : 两回事哦。共享空间是独立的,而线程如果我没记错,全局变量,包括文件内的(静态变量)是共享的。不同线程共享同一个进程内的变量嘛。这些和业务逻辑相关的东西,每个线程又是独立一套业务逻辑,针对c语言,这样去设计,不是没事找事嘛。面向对象语言,这块都帮你处理好了,自然没有关系。######既然有共享空间了,那你所说的进程和线程实际就是一回事了。###### @乌龟壳   ,数据分两种,一种和算法或处理相关的。一种是待处理的数据。 前者,不应该共享,后者属于数据加工流程,必然存在数据传递或流动,最低成本的传递/流动方式就是共享内存,交替使用权限的思路。 但这仅仅针对待加工的数据和辅助信息,而不针对程序本身。 进程不会搞混乱这些东西特别是(待加工数据的辅助信息),而线程,就各种乱吧。哈。 进程之间,虽然用共享空间,但它本质是数据传递/流动,当你采用多机(物理机器)并发处理时,进程移动到另外一个物理主机,则共享空间就是不能选择的传递/流动方式了。但线程就没有这些概念。 ######回复 @中山野鬼 : 是啊,java天然就不是像C一样对汇编的包装。######@乌龟壳 面向企业级的各种业务,java这些没问题的。而且更有优势,面向计算设备特性的设计开发,就不行了。哈。######回复 @中山野鬼 : 也算各有场景吧,java同样可以多进程可以分布式来降低多线程的风险。java也可以静态编译成目标机器码。总之事在人为。######回复 @乌龟壳 : 高手,啥都可以,低手,依赖这些,就是各种想当然。哈哈。######回复 @中山野鬼 : 那针对java的垃圾回收,这个东西是可以调节它算法的,不算依赖工具吧,哈。不然依赖C语言语法也算依赖工具咯。哈。;-p

kun坤 2020-05-31 13:04:51 0 浏览量 回答数 0

问题

2018MySQL技术问答集锦,希望能给喜欢MySQL的同学一些帮助

技术小能手 2019-12-01 19:31:11 1856 浏览量 回答数 0

回答

以下是我列出的2020年Java开发者应该学习的技术: 1、DevOps (Docker and Jenkins) 过去的一年,越来越多的公司正在转型DevOps,DevOps非常庞大,需要学习很多工具和原理,但你不需要担心。有大神已经分享了DevOps路线图(https://github.com/kamranahmedse/developer-roadmap,可以按照这个路线图以自己的速度学习和掌握DevOps。 如果你是一个有经验的Java程序员,愿意学习环境管理、自动化和整体改进,你也可以成为DevOps工程师。 2、Java 9 - Java 15 相信现在很多Java开发人员主要使用的Java版本还是以Java 8为主,虽然Java 9 - Java 13已经推出了有一段时间。 但是作为Java程序员,我们可能因为某些原因没办法在线上环境真正的进行JDK的升级,但是花一些时间学习Java 9、Java 10、Java 11、Java 12和 Java 13的新特性还是有必要的。 另外,大家可以重点关注一些关键特性,如GC相关的特性、对编码风格有改变的特性等。还有就是Java的LTS版本(Java 8、Java 11)要重点学习。 还要提醒大家一点,在2020年,Oracle还会推出Java 14 和 Java 15!!!如果你在使用Java 7的话,马上就要被"套圈"了! 3、Spring Framework 5 2017年我们见证了Spring和Java生态系统的许多重大升级,Spring 5.0就是其中之一。Spring 5 的新反应式编程模型、HTTP/2 支持,以及 Spring 通过 Kotlin 对函数式编程的全面支持这些都值得我们好好了解一下。 4、Spring Security 5.0 Spring Security 5.0 提供了许多新功能,并支持 Spring Framework 5.0,总共有 400 多个增强功能和 bug 修复。在Spring Security 5.0.0之前,密码是明文保存,十分不安全。因为这一次发布的是大版本,所以我们决定使用更安全的密码存储方式。Spring Security 5.0.0的主要亮点在于它只需要最小化的JDK 8、反应式安全特性、OAuth 2.0(OIDC)和现代密码存储。 5、Spring Boot 2 Spring Boot 2.0 基于 Spring 5 Framework ,提供了 异步非阻塞 IO 的响应式 Stream 、非堵塞的函数式 Reactive Web 框架 Spring WebFlux等特性。很多使用过SpringBoot的人都知道,使用SpringBoot搭建Web应用真的是又快又好,相信Spring Boot 2会带来更多惊喜。 6、Hadoop、Spark 和 Kafka 另外在2020年Java程序员需要学习的是大数据相关的知识。特别是Apache Spark 和 Kafka两个框架。 如果你也想在2020年学习大数据,也一定绕不开Hadoop生态。 7、Elasticsearch 全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选。维基百科、Stack Overflow、Github 都在使用它。 Elasticsearch是一个基于Lucene库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎,具有HTTP Web接口和无模式JSON文档。Elasticsearch是用Java开发的,并在Apache许可证下作为开源软件发布。 8、ServiceMesh 这两年很火,火的一塌糊涂。在2019年,但凡是程序员相关的大会,如果没有讲ServiceMest的专题,那都不好意思开。 所有人都在说 ServiceMesh; 几乎没人知道怎么落地 ServiceMesh; 但是大家都觉得其他人在大力做 ServiceMesh; 所以大家都宣称自己在做 ServiceMesh; 这个号称下一代微服务架构的概念,现在对于大多数人来说根本不知道是啥。只知道很多大厂宣称自己在做,很多大牛在布道。 9、Serverless 无服务器运算(英语:Serverless computing),又被称为功能即服务(Function-as-a-Service,缩写为 FaaS),是云计算的一种模型。以平台即服务(PaaS)为基础,无服务器运算提供一个微型的架构,终端客户不需要部署、配置或管理服务器服务,代码运行所需要的服务器服务皆由云平台来提供。这东西,听上去就很高大上。 2019年,和ServiceMesh一样,所有人都宣称自己在做。但是又很很多人不知道他到底是什么。 10、Kotlin 如果大家有关注Java 13的新特性的话,一定知道推出了字符串文本块的功能,这个功能其实是借鉴的Kotlin,除此之外,最近几年,Java有很多特性都在借鉴Kotlin,相比较于Java,Kotlin更加简洁,而且Kotlin编出来的代码也可以直接通过JVM运行。 Kotlin是一种在Java虚拟机上运行的静态类型编程语言,它也可以被编译成为JavaScript源代码。Kotlin的设计初衷就是用来生产高性能要求的程序的,所以运行起来和Java也是不相上下。Kotlin可以从 JetBrains InteilliJ Idea IDE这个开发工具以插件形式使用。 总结 以上,就是作者总结的建议Java程序员在2020年学习的一些技术,其中有一些是一定要学习的,还有一些是看大家的精力情况酌情考虑。

剑曼红尘 2020-04-07 20:42:52 0 浏览量 回答数 0

回答

你的难点在于Discuz是否可以向外开放用户认证接口,以及接口规范是啥。 这两个基础都没有,也就谈不到Gitlab接入的问题。 ######回复 @DuLerWeil : 现在一个网站用的是PHP,一个是Ruby,虽然都是用Nginx,能共存吗?######回复 @maya8maya85 : 程序咋部署无所谓,哪怕你把所有东西都装一台笔记本上。######回复 @DuLerWeil : 如果GitLab开放注册,那GitLab注册的帐号又要同步到Dz上,也是一大难题,现在简单点,单向同步,只把Dz的帐号同步过去,先搞定这个吧。你说这个oauth2服务器是又要开一个新的CentOS,还是放在旧的服务器上?######回复 @maya8maya85 : Gitlab可以开放注册啊。 关于Dz和Gitlab对接的问题,估计你是找不到现成的解决方案了。 鉴于OAuth2协议本身很简单,你可以研究Dz的用户表结构(特别是密码加盐算法之类的),然后基于表结构单独写个OAuth2服务器供Gitlab对接用。######GitLab不接受注册,只能在Dz注册再使用GitLab。我觉得这个好难,而且它们不在同一个服务器上,语言什么都不一样。有什么方法或者方向让我知道,我一步该怎么处理呢?###### Discuz应该是有个UCenter类似单点登录的,但很明显gitlab不会支持UCenter的协议。 楼主需要研究两个东西 UCenter的协议 ruby rails 然后参考我这篇文章:http://leo108.com/pid-2138.asp ######回复 @leo108 : 还有clone下来的omniauth如何安装?######回复 @leo108 : 这是通过套装安装的Gitlab######回复 @leo108 : 3,omniauth_allow_single_sign_on = true?默认是这”['saml']“,是bool值吗?######回复 @leo108 : 2,gitlab的config/initializers/devise.rb同样没有找到,/var,/opt都没有找到。######回复 @leo108 : 1,写好的mystrategy.rb放在哪里?根据你的指引一篇老外文章是放在"lib/strategies/mystrategy.rb",但根本没有"lib/strategies"这个目录。请问具体放在哪里?######这个要自己搞吧 没搞过 帮不上忙###### @maya8maya85  strategy.rb放/opt/gitlab/embedded/service/gitlab-rails/lib/omni_auth/strategies/ config/initializers/devise.rb在/opt/gitlab/embedded/service/gitlab-rails/config/initializers/devise.rb 至于omniauth_allow_single_sign_on,using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. clone安装的我没试过,不清楚 ######回复 @leo108 : C,最后一个就是这个strategy.rb这文件的文件名,好像除了写文件的时候出现了一下,其它地方从来都没看到它的名字,名字没关系吗?成功引用了吗?######回复 @leo108 : B,你那文章中的指明加载路径那段代码,是加在文件config/initializers/devise.rb那里,我加在最后,现在我不知道对不对?反正现在是502,是有个地方错了。######回复 @leo108 : A,这lib/omni_auth目录是没有strategies文件夹的,你的意思是新建文件夹放rb进去吗?######回复 @leo108 : 还有三个地方想请你指导一下。######回复 @maya8maya85 : gitlab本身已经集成了omniauth了,不需要你再装啊###### @leo108 ###### A. 新建文件夹 B.是真正的最后,在原本最下面的end那行下面 C.其实就是B里面的最后一个,也就是uco.rb ######回复 @leo108 : 调了几天都没成功,一错就是502,也不提示说有什么错误,不好调。。你那文章是没有发布gem的处理,如果是发布的gem,你会装吗?像安装个douban或者weibo什么的,能帮我装一下吗?大神,我按gitlab的文档也没成功######回复 @leo108 : 我比你更生手呢######回复 @maya8maya85 : 我对ruby也不熟,502的话应该是有ror的报错,具体日志路径在哪忘记了,你可以看看问题出在哪里######如果方便的话,我提供服务器相关密码,请你帮助一下,只要能调试到能重定向百度就ok.######特意抽出时间写了一天,按照你的文章指示,由于对语法不了解,我逐行调试,重编译,发现新写的uco.rb文件只要出现module就会502,不解。我安装的是8.10. devise.rb更是一加就502。不解,你的gitlab是什么版本?

kun坤 2020-06-01 11:10:31 0 浏览量 回答数 0

回答

你的难点在于Discuz是否可以向外开放用户认证接口,以及接口规范是啥。 这两个基础都没有,也就谈不到Gitlab接入的问题。 ######回复 @DuLerWeil : 现在一个网站用的是PHP,一个是Ruby,虽然都是用Nginx,能共存吗?######回复 @maya8maya85 : 程序咋部署无所谓,哪怕你把所有东西都装一台笔记本上。######回复 @DuLerWeil : 如果GitLab开放注册,那GitLab注册的帐号又要同步到Dz上,也是一大难题,现在简单点,单向同步,只把Dz的帐号同步过去,先搞定这个吧。你说这个oauth2服务器是又要开一个新的CentOS,还是放在旧的服务器上?######回复 @maya8maya85 : Gitlab可以开放注册啊。 关于Dz和Gitlab对接的问题,估计你是找不到现成的解决方案了。 鉴于OAuth2协议本身很简单,你可以研究Dz的用户表结构(特别是密码加盐算法之类的),然后基于表结构单独写个OAuth2服务器供Gitlab对接用。######GitLab不接受注册,只能在Dz注册再使用GitLab。我觉得这个好难,而且它们不在同一个服务器上,语言什么都不一样。有什么方法或者方向让我知道,我一步该怎么处理呢?###### Discuz应该是有个UCenter类似单点登录的,但很明显gitlab不会支持UCenter的协议。 楼主需要研究两个东西 UCenter的协议 ruby rails 然后参考我这篇文章:http://leo108.com/pid-2138.asp ######回复 @leo108 : 还有clone下来的omniauth如何安装?######回复 @leo108 : 这是通过套装安装的Gitlab######回复 @leo108 : 3,omniauth_allow_single_sign_on = true?默认是这”['saml']“,是bool值吗?######回复 @leo108 : 2,gitlab的config/initializers/devise.rb同样没有找到,/var,/opt都没有找到。######回复 @leo108 : 1,写好的mystrategy.rb放在哪里?根据你的指引一篇老外文章是放在"lib/strategies/mystrategy.rb",但根本没有"lib/strategies"这个目录。请问具体放在哪里?######这个要自己搞吧 没搞过 帮不上忙###### @maya8maya85  strategy.rb放/opt/gitlab/embedded/service/gitlab-rails/lib/omni_auth/strategies/ config/initializers/devise.rb在/opt/gitlab/embedded/service/gitlab-rails/config/initializers/devise.rb 至于omniauth_allow_single_sign_on,using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. clone安装的我没试过,不清楚 ######回复 @leo108 : C,最后一个就是这个strategy.rb这文件的文件名,好像除了写文件的时候出现了一下,其它地方从来都没看到它的名字,名字没关系吗?成功引用了吗?######回复 @leo108 : B,你那文章中的指明加载路径那段代码,是加在文件config/initializers/devise.rb那里,我加在最后,现在我不知道对不对?反正现在是502,是有个地方错了。######回复 @leo108 : A,这lib/omni_auth目录是没有strategies文件夹的,你的意思是新建文件夹放rb进去吗?######回复 @leo108 : 还有三个地方想请你指导一下。######回复 @maya8maya85 : gitlab本身已经集成了omniauth了,不需要你再装啊###### @leo108 ###### A. 新建文件夹 B.是真正的最后,在原本最下面的end那行下面 C.其实就是B里面的最后一个,也就是uco.rb ######回复 @leo108 : 调了几天都没成功,一错就是502,也不提示说有什么错误,不好调。。你那文章是没有发布gem的处理,如果是发布的gem,你会装吗?像安装个douban或者weibo什么的,能帮我装一下吗?大神,我按gitlab的文档也没成功######回复 @leo108 : 我比你更生手呢######回复 @maya8maya85 : 我对ruby也不熟,502的话应该是有ror的报错,具体日志路径在哪忘记了,你可以看看问题出在哪里######如果方便的话,我提供服务器相关密码,请你帮助一下,只要能调试到能重定向百度就ok.######特意抽出时间写了一天,按照你的文章指示,由于对语法不了解,我逐行调试,重编译,发现新写的uco.rb文件只要出现module就会502,不解。我安装的是8.10. devise.rb更是一加就502。不解,你的gitlab是什么版本?

kun坤 2020-06-14 11:44:53 0 浏览量 回答数 0

回答

" 你的难点在于<span style=""font-family:"font-size:14px;line-height:22px;background-color:#FFFFFF;"">Discuz是否可以向外开放用户认证接口,以及接口规范是啥。 这两个基础都没有,也就谈不到Gitlab接入的问题。 ######回复 @DuLerWeil : 现在一个网站用的是PHP,一个是Ruby,虽然都是用Nginx,能共存吗?######回复 @maya8maya85 : 程序咋部署无所谓,哪怕你把所有东西都装一台笔记本上。######回复 @DuLerWeil : 如果GitLab开放注册,那GitLab注册的帐号又要同步到Dz上,也是一大难题,现在简单点,单向同步,只把Dz的帐号同步过去,先搞定这个吧。你说这个oauth2服务器是又要开一个新的CentOS,还是放在旧的服务器上?######回复 @maya8maya85 : Gitlab可以开放注册啊。 关于Dz和Gitlab对接的问题,估计你是找不到现成的解决方案了。 鉴于OAuth2协议本身很简单,你可以研究Dz的用户表结构(特别是密码加盐算法之类的),然后基于表结构单独写个OAuth2服务器供Gitlab对接用。######GitLab不接受注册,只能在Dz注册再使用GitLab。我觉得这个好难,而且它们不在同一个服务器上,语言什么都不一样。有什么方法或者方向让我知道,我一步该怎么处理呢?###### Discuz应该是有个UCenter类似单点登录的,但很明显gitlab不会支持UCenter的协议。 楼主需要研究两个东西 UCenter的协议 ruby rails 然后参考我这篇文章:http://leo108.com/pid-2138.asp ######回复 @leo108 : 还有clone下来的omniauth如何安装?######回复 @leo108 : 这是通过套装安装的Gitlab######回复 @leo108 : 3,omniauth_allow_single_sign_on = true?默认是这”['saml']“,是bool值吗?######回复 @leo108 : 2,gitlab的config/initializers/devise.rb同样没有找到,/var,/opt都没有找到。######回复 @leo108 : 1,写好的mystrategy.rb放在哪里?根据你的指引一篇老外文章是放在"lib/strategies/mystrategy.rb",但根本没有"lib/strategies"这个目录。请问具体放在哪里?######这个要自己搞吧 没搞过 帮不上忙###### @maya8maya85  strategy.rb放/opt/gitlab/embedded/service/gitlab-rails/lib/omni_auth/strategies/ config/initializers/devise.rb在/opt/gitlab/embedded/service/gitlab-rails/config/initializers/devise.rb 至于omniauth_allow_single_sign_on,using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. clone安装的我没试过,不清楚 ######回复 @leo108 : C,最后一个就是这个strategy.rb这文件的文件名,好像除了写文件的时候出现了一下,其它地方从来都没看到它的名字,名字没关系吗?成功引用了吗?######回复 @leo108 : B,你那文章中的指明加载路径那段代码,是加在文件config/initializers/devise.rb那里,我加在最后,现在我不知道对不对?反正现在是502,是有个地方错了。######回复 @leo108 : A,这lib/omni_auth目录是没有strategies文件夹的,你的意思是新建文件夹放rb进去吗?######回复 @leo108 : 还有三个地方想请你指导一下。######回复 @maya8maya85 : gitlab本身已经集成了omniauth了,不需要你再装啊###### @leo108 ###### A. 新建文件夹 B.是真正的最后,在原本最下面的end那行下面 C.其实就是B里面的最后一个,也就是uco.rb ######回复 @leo108 : 调了几天都没成功,一错就是502,也不提示说有什么错误,不好调。。你那文章是没有发布gem的处理,如果是发布的gem,你会装吗?像安装个douban或者weibo什么的,能帮我装一下吗?大神,我按gitlab的文档也没成功######回复 @leo108 : 我比你更生手呢######回复 @maya8maya85 : 我对ruby也不熟,502的话应该是有ror的报错,具体日志路径在哪忘记了,你可以看看问题出在哪里######如果方便的话,我提供服务器相关密码,请你帮助一下,只要能调试到能重定向百度就ok.######特意抽出时间写了一天,按照你的文章指示,由于对语法不了解,我逐行调试,重编译,发现新写的uco.rb文件只要出现module就会502,不解。我安装的是8.10. devise.rb更是一加就502。不解,你的gitlab是什么版本?"

montos 2020-06-03 15:13:03 0 浏览量 回答数 0

回答

" 你的难点在于<span style=""font-family:"font-size:14px;line-height:22px;background-color:#FFFFFF;"">Discuz是否可以向外开放用户认证接口,以及接口规范是啥。 这两个基础都没有,也就谈不到Gitlab接入的问题。 ######回复 @DuLerWeil : 现在一个网站用的是PHP,一个是Ruby,虽然都是用Nginx,能共存吗?######回复 @maya8maya85 : 程序咋部署无所谓,哪怕你把所有东西都装一台笔记本上。######回复 @DuLerWeil : 如果GitLab开放注册,那GitLab注册的帐号又要同步到Dz上,也是一大难题,现在简单点,单向同步,只把Dz的帐号同步过去,先搞定这个吧。你说这个oauth2服务器是又要开一个新的CentOS,还是放在旧的服务器上?######回复 @maya8maya85 : Gitlab可以开放注册啊。 关于Dz和Gitlab对接的问题,估计你是找不到现成的解决方案了。 鉴于OAuth2协议本身很简单,你可以研究Dz的用户表结构(特别是密码加盐算法之类的),然后基于表结构单独写个OAuth2服务器供Gitlab对接用。######GitLab不接受注册,只能在Dz注册再使用GitLab。我觉得这个好难,而且它们不在同一个服务器上,语言什么都不一样。有什么方法或者方向让我知道,我一步该怎么处理呢?###### Discuz应该是有个UCenter类似单点登录的,但很明显gitlab不会支持UCenter的协议。 楼主需要研究两个东西 UCenter的协议 ruby rails 然后参考我这篇文章:http://leo108.com/pid-2138.asp ######回复 @leo108 : 还有clone下来的omniauth如何安装?######回复 @leo108 : 这是通过套装安装的Gitlab######回复 @leo108 : 3,omniauth_allow_single_sign_on = true?默认是这”['saml']“,是bool值吗?######回复 @leo108 : 2,gitlab的config/initializers/devise.rb同样没有找到,/var,/opt都没有找到。######回复 @leo108 : 1,写好的mystrategy.rb放在哪里?根据你的指引一篇老外文章是放在"lib/strategies/mystrategy.rb",但根本没有"lib/strategies"这个目录。请问具体放在哪里?######这个要自己搞吧 没搞过 帮不上忙###### @maya8maya85  strategy.rb放/opt/gitlab/embedded/service/gitlab-rails/lib/omni_auth/strategies/ config/initializers/devise.rb在/opt/gitlab/embedded/service/gitlab-rails/config/initializers/devise.rb 至于omniauth_allow_single_sign_on,using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. clone安装的我没试过,不清楚 ######回复 @leo108 : C,最后一个就是这个strategy.rb这文件的文件名,好像除了写文件的时候出现了一下,其它地方从来都没看到它的名字,名字没关系吗?成功引用了吗?######回复 @leo108 : B,你那文章中的指明加载路径那段代码,是加在文件config/initializers/devise.rb那里,我加在最后,现在我不知道对不对?反正现在是502,是有个地方错了。######回复 @leo108 : A,这lib/omni_auth目录是没有strategies文件夹的,你的意思是新建文件夹放rb进去吗?######回复 @leo108 : 还有三个地方想请你指导一下。######回复 @maya8maya85 : gitlab本身已经集成了omniauth了,不需要你再装啊###### @leo108 ###### A. 新建文件夹 B.是真正的最后,在原本最下面的end那行下面 C.其实就是B里面的最后一个,也就是uco.rb ######回复 @leo108 : 调了几天都没成功,一错就是502,也不提示说有什么错误,不好调。。你那文章是没有发布gem的处理,如果是发布的gem,你会装吗?像安装个douban或者weibo什么的,能帮我装一下吗?大神,我按gitlab的文档也没成功######回复 @leo108 : 我比你更生手呢######回复 @maya8maya85 : 我对ruby也不熟,502的话应该是有ror的报错,具体日志路径在哪忘记了,你可以看看问题出在哪里######如果方便的话,我提供服务器相关密码,请你帮助一下,只要能调试到能重定向百度就ok.######特意抽出时间写了一天,按照你的文章指示,由于对语法不了解,我逐行调试,重编译,发现新写的uco.rb文件只要出现module就会502,不解。我安装的是8.10. devise.rb更是一加就502。不解,你的gitlab是什么版本?"

montos 2020-05-31 21:56:24 0 浏览量 回答数 0

回答

PHP面试干货 1、进程和线程 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 2、apache默认使用进程管理还是线程管理?如何判断并设置最大连接数? 一个进程可以开多个线程 默认是进程管理 默认有一个主进程 Linux: ps -aux | grep httpd | more 一个子进程代表一个用户的连接 Conf/extra/httpd-mpm.conf 多路功能模块 http -l 查询当前apache处于什么模式下 3、单例模式 单例模式需求:只能实例化产生一个对象 如何实现: 私有化构造函数 禁止克隆对象 提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一对象 需要一个保存类的静态属性 class demo { private static $MyObject; //保存对象的静态属性 private function __construct(){ //私有化构造函数 } private function __clone(){ //禁止克隆 } public static function getInstance(){ if(! (self::$MyObject instanceof self)){ self::$MyObject = new self; } return self::$MyObject; } } 4、安装完Apache后,在http.conf中配置加载PHP文件以Apache模块的方式安装PHP,在文件http.conf中首先要用语句LoadModule php5_module "e:/php/php5apache2.dll"动态装载PHP模块,然后再用语句AddType application/x-httpd-php .php 使得Apache把所有扩展名为PHP的文件都作为PHP脚本处理 5、debug_backtrace()函数能返回脚本里的任意行中调用的函数的名称。该函数同时还经常被用在调试中,用来判断错误是如何发生的 function one($str1, $str2) { two("Glenn", "Quagmire"); } function two($str1, $str2) { three("Cleveland", "Brown"); } function three($str1, $str2) { print_r(debug_backtrace()); } one("Peter", "Griffin"); Array ( [0] => Array ( [file] => D:\www\test\result.php [line] => 9 [function] => three [args] => Array ( [0] => Cleveland [1] => Brown ) ) [1] => Array ( [file] => D:\www\test\result.php [line] => 5 [function] => two [args] => Array ( [0] => Glenn [1] => Quagmire ) ) [2] => Array ( [file] => D:\www\test\result.php [line] => 16 [function] => one [args] => Array ( [0] => Peter [1] => Griffin ) ) ) 6、输出用户的IP地址,并且判断用户的IP地址是否在192.168.1.100 — 192.168.1.150之间 echo $ip=getenv('REMOTE_ADDR'); $ip=str_replace('.','',$ip); if($ip<1921681150 && $ip>1921681100) { echo 'ip在192.168.1.100—–192.168.1.150之间'; } else { echo 'ip不在192.168.1.100—–192.168.1.150之间'; } 7、请将2维数组按照name的长度进行重新排序,按照顺序将id赋值 $tarray = array( array('id' => 0, 'name' => '123'), array('id' => 0, 'name' => '1234'), array('id' => 0, 'name' => '1235'), array('id' => 0, 'name' => '12356'), array('id' => 0, 'name' => '123abc') ); foreach($tarray as $key=>$val) { $c[]=$val['name']; } function aa($a,$b) { if(strlen($a)==strlen($b)) return 0; return strlen($a)>strlen($b)?-1:1; } usort($c,'aa'); $len=count($c); for($i=0;$i<$len;$i++) { $t[$i]['id']=$i+1; $t[$i]['name']=$c[$i]; } print_r($t); 8、表单数据提交方式POST和GET的区别,URL地址传递的数据最大长度是多少? POST方式提交数据用户不可见,是数据更安全,最大长度不受限制,而GET方式传值在URL地址可以看到,相对不安全,对大长度是2048字节。 9、SESSION和COOKIE的作用和区别,SESSION信息的存储方式,如何进行遍历 SESSION和COOKIE都能够使值在页面之间进行传递,SESSION存储在服务器端,数据更安全,COOKIE保存在客户端,用户使用手段可以进行修改,SESSION依赖于COOKIE进行传递的。Session遍历使用$_SESSION[]取值,cookie遍历使用$_COOKIE[]取值。 10、什么是数据库索引,主键索引,唯一索引的区别,索引的缺点是什么 索引用来快速地寻找那些具有特定值的记录。 主键索引和唯一索引的区别:主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”,每个表只能有一个主键。唯一索引索引列的所有值都只能出现一次,即必须唯一。 索引的缺点: 1、创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 2、索引需要占用物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,需要的空间就会更大。 3、当对表中的数据进行增加、删除、修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。 11、数据库设计时,常遇到的性能瓶颈有哪些,常有的解决方案 瓶颈主要有: 1、磁盘搜索 优化方法是:将数据分布在多个磁盘上 2、磁盘读/写 优化方法是:从多个磁盘并行读写。 3、CPU周期 优化方法:扩充内存 4、内存带宽 12、include和require区别 include引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。 require引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。 13、文件上传时设计到点 和文件上传有关的php.ini配置选项(File Uploads): file_uploads=On/Off:文件是否允许上传 upload_max_filesize上传文件时,单个文件的最大大小 post_max_size:提交表单时,整个post表单的最大大小 max_file_uploads =20上传文件的个数 内存占用,脚本最大执行时间也间接影响到文件的上传 14、header常见状态 //200 正常状态 header('HTTP/1.1 200 OK'); // 301 永久重定向,记得在后面要加重定向地址 Location:$url header('HTTP/1.1 301 Moved Permanently'); // 重定向,其实就是302 暂时重定向 header('Location: http://www.maiyoule.com/'); // 设置页面304 没有修改 header('HTTP/1.1 304 Not Modified'); // 显示登录框, header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Basic realm="登录信息"'); echo '显示的信息!'; // 403 禁止访问 header('HTTP/1.1 403 Forbidden'); // 404 错误 header('HTTP/1.1 404 Not Found'); // 500 服务器错误 header('HTTP/1.1 500 Internal Server Error'); // 3秒后重定向指定地址(也就是刷新到新页面与 <meta http-equiv="refresh" content="10;http://www.maiyoule.com/ /> 相同) header('Refresh: 3; url=http://www.maiyoule.com/'); echo '10后跳转到http://www.maiyoule.com'; // 重写 X-Powered-By 值 header('X-Powered-By: PHP/5.3.0'); header('X-Powered-By: Brain/0.6b'); //设置上下文语言 header('Content-language: en'); // 设置页面最后修改时间(多用于防缓存) $time = time() - 60; //建议使用filetime函数来设置页面缓存时间 header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); // 设置内容长度 header('Content-Length: 39344'); // 设置头文件类型,可以用于流文件或者文件下载 header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="example.zip"'); header('Content-Transfer-Encoding: binary'); readfile('example.zip');//读取文件到客户端 //禁用页面缓存 header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Pragma: no-cache'); //设置页面头信息 header('Content-Type: text/html; charset=iso-8859-1'); header('Content-Type: text/html; charset=utf-8'); header('Content-Type: text/plain'); header('Content-Type: image/jpeg'); header('Content-Type: application/zip'); header('Content-Type: application/pdf'); header('Content-Type: audio/mpeg'); header('Content-Type: application/x-shockwave-flash'); //.... 至于Content-Type 的值 可以去查查 w3c 的文档库,那里很丰富 15、ORM和ActiveRecord ORM:object relation mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射。为什么要有这么一个映射?很简单,因为现在的开发语言基本都是oop的,但是传统的数据库却是关系型的。为了可以靠贴近面向对象开发,我们想要像操作对象一样操作数据库。还可以隔离底层数据库层,我们不需要关心我们使用的是mysql还是其他的关系型数据库 ActiveRecord也属于ORM层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。 ActiveRecord的主要思想是: 1. 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field; 2. ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;; 3. ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑; ActiveRecord比较适用于: 1. 业务逻辑比较简单,当你的类基本上和数据库中的表一一对应时, ActiveRecord是非常方便的,即你的业务逻辑大多数是对单表操作; 2. 当发生跨表的操作时, 往往会配合使用事务脚本(Transaction Script),把跨表事务提升到事务脚本中; 3. ActiveRecord最大优点是简单, 直观。 一个类就包括了数据访问和业务逻辑. 如果配合代码生成器使用就更方便了; 这些优点使ActiveRecord特别适合WEB快速开发。 16、斐波那契方法,也就是1 1 2 3 5 8 ……,这里给出两种方法,大家可以对比下,看看哪种快,以及为什么 function fibonacci($n){ if($n == 0){ return 0; } if($n == 1){ return 1; } return fibonacci($n-1)+fibonacci($n-2); } function fibonacci($n){ for($i=0; $i<$n; $i++){ $r[] = $i<2 ? 1 : $r[$i-1]+$r[$i-2]; } return $r[--$i]; } 17、约瑟夫环,也就是常见的数猴子,n只猴子围成一圈,每只猴子下面标了编号,从1开始数起,数到m那么第m只猴子便退出,依次类推,每数到m,那么那个位置的猴子退出,那么最后剩下的猴子下的编号是啥。 function yuesefu($n,$m) { $r=0; for($i=2; $i<=$n; $i++) { $r=($r+$m)%$i; } return $r+1; } 18、冒泡排序,大致是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,然后再从头开始进行两两比较交换,直到倒数第二位时结束 function bubbleSort($arr){ for($i=0, $len=count($arr); $i<$len; $i++){ for($j=0; $j<$len; $j++){ if($arr[$i]<$arr[$j]){ $tmp = $arr[$j]; $arr[$j] = $arr[$i]; $arr[$i] = $tmp; } } } return $arr; } 19、快速排序,也就是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。 function quickSort($arr){ $len = count($arr); if($len <=1){ return $arr; } $key = $arr[0]; $leftArr = $rightArr= array(); for($i=1; $i<$len; $i++){ if($arr[$i] <= $key){ $leftArr[] = $arr[$i]; } else{ $rightArr[] = $arr[$i]; } } $leftArr = quickSort($leftArr); $rightArr = quickSort($rightArr); return array_merge($leftArr, array($key), $rightArr); } 20、(递归的)列出目录下所有文件及目录,这里也有两种方法 function listDir($path){ $res = dir($path); while($file = $res->read()){ if($file == '.' || $file == '..'){ continue; } if(is_dir($path . '/' .$file)){ echo $path . '/' .$file . "\r\n"; listDir($path . '/' .$file); } else{ echo $path . '/' .$file . "\r\n"; } } $res->close(); } function listDir($path){ if(is_dir($path)){ if(FALSE !== ($res = opendir($path))){ while(FALSE !== ($file = readdir($res))){ if($file == '.' || $file == '..'){ continue; } $subPath = $path . '/' . $file; if(is_dir($subPath)){ echo $subPath . "\r\n"; listDir($subPath); } else{ echo $subPath . "\r\n"; } } } } } 21、找出相对的目录,比如/a/b/c/d/e.php相对于/a/b/13/34/c.php是/c/d/ function ralativePath($a, $b){ $a = explode('/', dirname($a)); $b = explode('/', dirname($b)); $c = '/'; foreach ($a as $k=> $v){ if($v != $b[$k]){ $c .= $v . '/'; } } echo $c; } 22、快速找出url中php后缀 function get_ext($url){ $data = parse_url($url); return pathinfo($data['path'], PATHINFO_EXTENSION); } 23、正则题,使用正则抓取网页,以网页meta为utf8为准,若是抓取的网页编码为big5之类的,需要转化为utf8再收录 function preg_meta($meta){ $replacement = "\\1utf8\\6\\7"; $pattern = '#(<meta\s+http-equiv=(\'|"|)Content-Type(\'|"|)\s+content=(\'|"|)text/html; charset=)(\w+)(\'|"|)(>)#i'; return preg_replace($pattern, $replacement, $meta); } echo preg_meta("<meta http-equiv=Content-Type content='text/html; charset=big5'><META http-equiv=\"Content-Type\" content='text/html; charset=big5'>"); 24、不用php的反转函数倒序输出字符串,如abc,反序输出cba function revstring($str){ for($i=strlen($str)-1; $i>=0; $i--){ echo $str{$i}; } } revstring('abc'); 25、常见端口 TCP 21端口:FTP 文件传输服务 SSH 22端口:SSH连接linux服务器,通过SSH连接可以远程管理Linux等设备 TCP 23端口:TELNET 终端仿真服务 TCP 25端口:SMTP 简单邮件传输服务 UDP 53端口:DNS 域名解析服务 TCP 80端口:HTTP 超文本传输服务 TCP 110端口:POP3 “邮局协议版本3”使用的端口 TCP 443端口:HTTPS 加密的超文本传输服务 TCP 1521端口:Oracle数据库服务 TCP 1863端口:MSN Messenger的文件传输功能所使用的端口 TCP 3389端口:Microsoft RDP 微软远程桌面使用的端口 TCP 5631端口:Symantec pcAnywhere 远程控制数据传输时使用的端口 UDP 5632端口:Symantec pcAnywhere 主控端扫描被控端时使用的端口 TCP 5000端口:MS SQL Server使用的端口 UDP 8000端口:腾讯QQ 26、linux常用的命令 top linux进程实时监控 ps 在Linux中是查看进程的命令。ps查看正处于Running的进程 mv 为文件或目录改名或将文件由一个目录移入另一个目录中。 find 查找文件 df 可显示所有文件系统对i节点和磁盘块的使用情况。 cat 打印文件类容 chmod 变更文件或目录的权限 chgrp 文件或目录的权限的掌控以拥有者及所诉群组来管理。可以使用chgrp指令取变更文件与目录所属群组 grep 是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。 wc 为统计指定文件中的字节数、字数、行数,并将统计结果显示输出 27、对于大流量的网站,您采用什么样的方法来解决访问量问题 首先,确认服务器硬件是否足够支持当前的流量 其次,优化数据库访问。 第三,禁止外部的盗链。 第四,控制大文件的下载。 第五,使用不同主机分流主要流量 第六,使用流量分析统计软件 28、$_SERVER常用的字段 $_SERVER['PHP_SELF'] #当前正在执行脚本的文件名 $_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称 $_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT” $_SERVER['QUERY_STRING'] #查询(query)的字符串 $_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容 $_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址 $_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址 $_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名 $_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名 $_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用 $_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html” 29、安装php扩展 进入扩展的目录 phpize命令得到configure文件 ./configure --with-php-config=/usr/local/php/bin/php-config make & make install 在php.ini中加入扩展名称.so 重启web服务器(nginx/apache) 30、php-fpm与nginx PHP-FPM也是一个第三方的FastCGI进程管理器,它是作为PHP的一个补丁来开发的,在安装的时候也需要和PHP源码一起编译,也就是说PHP-FPM被编译到PHP内核中,因此在处理性能方面更加优秀;同时它在处理高并发方面也比spawn-fcgi引擎好很多,因此,推荐Nginx+PHP/PHP-FPM这个组合对PHP进行解析。 FastCGI 的主要优点是把动态语言和HTTP Server分离开来,所以Nginx与PHP/PHP-FPM经常被部署在不同的服务器上,以分担前端Nginx服务器的压力,使Nginx专一处理静态请求和转发动态请求,而PHP/PHP-FPM服务器专一解析PHP动态请求 #fastcgi FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等,同时,FastCGI也被许多脚本语言所支持,其中就有PHP。 FastCGI是从CGI发展改进而来的。传统CGI接口方式的主要缺点是性能很差,因为每次HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后结果被返回给HTTP服务器。这在处理高并发访问时,几乎是不可用的。另外传统的CGI接口方式安全性也很差,现在已经很少被使用了。 FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。 Nginx+FastCGI运行原理 Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket,(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接纳到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端,这就是Nginx+FastCGI的整个运作过程。 31、ajax全称“Asynchronous Javascript And XML”(异步JavaScript和XML)

小川游鱼 2019-12-02 01:41:29 0 浏览量 回答数 0

回答

PHP面试干货 1、进程和线程 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 2、apache默认使用进程管理还是线程管理?如何判断并设置最大连接数? 一个进程可以开多个线程 默认是进程管理 默认有一个主进程 Linux: ps -aux | grep httpd | more 一个子进程代表一个用户的连接 Conf/extra/httpd-mpm.conf 多路功能模块 http -l 查询当前apache处于什么模式下 3、单例模式 单例模式需求:只能实例化产生一个对象 如何实现: 私有化构造函数 禁止克隆对象 提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一对象 需要一个保存类的静态属性 class demo { private static $MyObject; //保存对象的静态属性 private function __construct(){ //私有化构造函数 } private function __clone(){ //禁止克隆 } public static function getInstance(){ if(! (self::$MyObject instanceof self)){ self::$MyObject = new self; } return self::$MyObject; } } 4、安装完Apache后,在http.conf中配置加载PHP文件以Apache模块的方式安装PHP,在文件http.conf中首先要用语句LoadModule php5_module "e:/php/php5apache2.dll"动态装载PHP模块,然后再用语句AddType application/x-httpd-php .php 使得Apache把所有扩展名为PHP的文件都作为PHP脚本处理 5、debug_backtrace()函数能返回脚本里的任意行中调用的函数的名称。该函数同时还经常被用在调试中,用来判断错误是如何发生的 function one($str1, $str2) { two("Glenn", "Quagmire"); } function two($str1, $str2) { three("Cleveland", "Brown"); } function three($str1, $str2) { print_r(debug_backtrace()); } one("Peter", "Griffin"); Array ( [0] => Array ( [file] => D:\www\test\result.php [line] => 9 [function] => three [args] => Array ( [0] => Cleveland [1] => Brown ) ) [1] => Array ( [file] => D:\www\test\result.php [line] => 5 [function] => two [args] => Array ( [0] => Glenn [1] => Quagmire ) ) [2] => Array ( [file] => D:\www\test\result.php [line] => 16 [function] => one [args] => Array ( [0] => Peter [1] => Griffin ) ) ) 6、输出用户的IP地址,并且判断用户的IP地址是否在192.168.1.100 — 192.168.1.150之间 echo $ip=getenv('REMOTE_ADDR'); $ip=str_replace('.','',$ip); if($ip<1921681150 && $ip>1921681100) { echo 'ip在192.168.1.100—–192.168.1.150之间'; } else { echo 'ip不在192.168.1.100—–192.168.1.150之间'; } 7、请将2维数组按照name的长度进行重新排序,按照顺序将id赋值 $tarray = array( array('id' => 0, 'name' => '123'), array('id' => 0, 'name' => '1234'), array('id' => 0, 'name' => '1235'), array('id' => 0, 'name' => '12356'), array('id' => 0, 'name' => '123abc') ); foreach($tarray as $key=>$val) { $c[]=$val['name']; } function aa($a,$b) { if(strlen($a)==strlen($b)) return 0; return strlen($a)>strlen($b)?-1:1; } usort($c,'aa'); $len=count($c); for($i=0;$i<$len;$i++) { $t[$i]['id']=$i+1; $t[$i]['name']=$c[$i]; } print_r($t); 8、表单数据提交方式POST和GET的区别,URL地址传递的数据最大长度是多少? POST方式提交数据用户不可见,是数据更安全,最大长度不受限制,而GET方式传值在URL地址可以看到,相对不安全,对大长度是2048字节。 9、SESSION和COOKIE的作用和区别,SESSION信息的存储方式,如何进行遍历 SESSION和COOKIE都能够使值在页面之间进行传递,SESSION存储在服务器端,数据更安全,COOKIE保存在客户端,用户使用手段可以进行修改,SESSION依赖于COOKIE进行传递的。Session遍历使用$_SESSION[]取值,cookie遍历使用$_COOKIE[]取值。 10、什么是数据库索引,主键索引,唯一索引的区别,索引的缺点是什么 索引用来快速地寻找那些具有特定值的记录。 主键索引和唯一索引的区别:主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”,每个表只能有一个主键。唯一索引索引列的所有值都只能出现一次,即必须唯一。 索引的缺点: 1、创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 2、索引需要占用物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,需要的空间就会更大。 3、当对表中的数据进行增加、删除、修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。 11、数据库设计时,常遇到的性能瓶颈有哪些,常有的解决方案 瓶颈主要有: 1、磁盘搜索 优化方法是:将数据分布在多个磁盘上 2、磁盘读/写 优化方法是:从多个磁盘并行读写。 3、CPU周期 优化方法:扩充内存 4、内存带宽 12、include和require区别 include引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。 require引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。 13、文件上传时设计到点 和文件上传有关的php.ini配置选项(File Uploads): file_uploads=On/Off:文件是否允许上传 upload_max_filesize上传文件时,单个文件的最大大小 post_max_size:提交表单时,整个post表单的最大大小 max_file_uploads =20上传文件的个数 内存占用,脚本最大执行时间也间接影响到文件的上传 14、header常见状态 //200 正常状态 header('HTTP/1.1 200 OK'); // 301 永久重定向,记得在后面要加重定向地址 Location:$url header('HTTP/1.1 301 Moved Permanently'); // 重定向,其实就是302 暂时重定向 header('Location: http://www.maiyoule.com/'); // 设置页面304 没有修改 header('HTTP/1.1 304 Not Modified'); // 显示登录框, header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Basic realm="登录信息"'); echo '显示的信息!'; // 403 禁止访问 header('HTTP/1.1 403 Forbidden'); // 404 错误 header('HTTP/1.1 404 Not Found'); // 500 服务器错误 header('HTTP/1.1 500 Internal Server Error'); // 3秒后重定向指定地址(也就是刷新到新页面与 <meta http-equiv="refresh" content="10;http://www.maiyoule.com/ /> 相同) header('Refresh: 3; url=http://www.maiyoule.com/'); echo '10后跳转到http://www.maiyoule.com'; // 重写 X-Powered-By 值 header('X-Powered-By: PHP/5.3.0'); header('X-Powered-By: Brain/0.6b'); //设置上下文语言 header('Content-language: en'); // 设置页面最后修改时间(多用于防缓存) $time = time() - 60; //建议使用filetime函数来设置页面缓存时间 header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); // 设置内容长度 header('Content-Length: 39344'); // 设置头文件类型,可以用于流文件或者文件下载 header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="example.zip"'); header('Content-Transfer-Encoding: binary'); readfile('example.zip');//读取文件到客户端 //禁用页面缓存 header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Pragma: no-cache'); //设置页面头信息 header('Content-Type: text/html; charset=iso-8859-1'); header('Content-Type: text/html; charset=utf-8'); header('Content-Type: text/plain'); header('Content-Type: image/jpeg'); header('Content-Type: application/zip'); header('Content-Type: application/pdf'); header('Content-Type: audio/mpeg'); header('Content-Type: application/x-shockwave-flash'); //.... 至于Content-Type 的值 可以去查查 w3c 的文档库,那里很丰富 15、ORM和ActiveRecord ORM:object relation mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射。为什么要有这么一个映射?很简单,因为现在的开发语言基本都是oop的,但是传统的数据库却是关系型的。为了可以靠贴近面向对象开发,我们想要像操作对象一样操作数据库。还可以隔离底层数据库层,我们不需要关心我们使用的是mysql还是其他的关系型数据库 ActiveRecord也属于ORM层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。 ActiveRecord的主要思想是: 1. 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field; 2. ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;; 3. ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑; ActiveRecord比较适用于: 1. 业务逻辑比较简单,当你的类基本上和数据库中的表一一对应时, ActiveRecord是非常方便的,即你的业务逻辑大多数是对单表操作; 2. 当发生跨表的操作时, 往往会配合使用事务脚本(Transaction Script),把跨表事务提升到事务脚本中; 3. ActiveRecord最大优点是简单, 直观。 一个类就包括了数据访问和业务逻辑. 如果配合代码生成器使用就更方便了; 这些优点使ActiveRecord特别适合WEB快速开发。 16、斐波那契方法,也就是1 1 2 3 5 8 ……,这里给出两种方法,大家可以对比下,看看哪种快,以及为什么 function fibonacci($n){ if($n == 0){ return 0; } if($n == 1){ return 1; } return fibonacci($n-1)+fibonacci($n-2); } function fibonacci($n){ for($i=0; $i<$n; $i++){ $r[] = $i<2 ? 1 : $r[$i-1]+$r[$i-2]; } return $r[--$i]; } 17、约瑟夫环,也就是常见的数猴子,n只猴子围成一圈,每只猴子下面标了编号,从1开始数起,数到m那么第m只猴子便退出,依次类推,每数到m,那么那个位置的猴子退出,那么最后剩下的猴子下的编号是啥。 function yuesefu($n,$m) { $r=0; for($i=2; $i<=$n; $i++) { $r=($r+$m)%$i; } return $r+1; } 18、冒泡排序,大致是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,然后再从头开始进行两两比较交换,直到倒数第二位时结束 function bubbleSort($arr){ for($i=0, $len=count($arr); $i<$len; $i++){ for($j=0; $j<$len; $j++){ if($arr[$i]<$arr[$j]){ $tmp = $arr[$j]; $arr[$j] = $arr[$i]; $arr[$i] = $tmp; } } } return $arr; } 19、快速排序,也就是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。 function quickSort($arr){ $len = count($arr); if($len <=1){ return $arr; } $key = $arr[0]; $leftArr = $rightArr= array(); for($i=1; $i<$len; $i++){ if($arr[$i] <= $key){ $leftArr[] = $arr[$i]; } else{ $rightArr[] = $arr[$i]; } } $leftArr = quickSort($leftArr); $rightArr = quickSort($rightArr); return array_merge($leftArr, array($key), $rightArr); } 20、(递归的)列出目录下所有文件及目录,这里也有两种方法 function listDir($path){ $res = dir($path); while($file = $res->read()){ if($file == '.' || $file == '..'){ continue; } if(is_dir($path . '/' .$file)){ echo $path . '/' .$file . "\r\n"; listDir($path . '/' .$file); } else{ echo $path . '/' .$file . "\r\n"; } } $res->close(); } function listDir($path){ if(is_dir($path)){ if(FALSE !== ($res = opendir($path))){ while(FALSE !== ($file = readdir($res))){ if($file == '.' || $file == '..'){ continue; } $subPath = $path . '/' . $file; if(is_dir($subPath)){ echo $subPath . "\r\n"; listDir($subPath); } else{ echo $subPath . "\r\n"; } } } } } 21、找出相对的目录,比如/a/b/c/d/e.php相对于/a/b/13/34/c.php是/c/d/ function ralativePath($a, $b){ $a = explode('/', dirname($a)); $b = explode('/', dirname($b)); $c = '/'; foreach ($a as $k=> $v){ if($v != $b[$k]){ $c .= $v . '/'; } } echo $c; } 22、快速找出url中php后缀 function get_ext($url){ $data = parse_url($url); return pathinfo($data['path'], PATHINFO_EXTENSION); } 23、正则题,使用正则抓取网页,以网页meta为utf8为准,若是抓取的网页编码为big5之类的,需要转化为utf8再收录 function preg_meta($meta){ $replacement = "\\1utf8\\6\\7"; $pattern = '#(<meta\s+http-equiv=(\'|"|)Content-Type(\'|"|)\s+content=(\'|"|)text/html; charset=)(\w+)(\'|"|)(>)#i'; return preg_replace($pattern, $replacement, $meta); } echo preg_meta("<meta http-equiv=Content-Type content='text/html; charset=big5'><META http-equiv=\"Content-Type\" content='text/html; charset=big5'>"); 24、不用php的反转函数倒序输出字符串,如abc,反序输出cba function revstring($str){ for($i=strlen($str)-1; $i>=0; $i--){ echo $str{$i}; } } revstring('abc'); 25、常见端口 TCP 21端口:FTP 文件传输服务 SSH 22端口:SSH连接linux服务器,通过SSH连接可以远程管理Linux等设备 TCP 23端口:TELNET 终端仿真服务 TCP 25端口:SMTP 简单邮件传输服务 UDP 53端口:DNS 域名解析服务 TCP 80端口:HTTP 超文本传输服务 TCP 110端口:POP3 “邮局协议版本3”使用的端口 TCP 443端口:HTTPS 加密的超文本传输服务 TCP 1521端口:Oracle数据库服务 TCP 1863端口:MSN Messenger的文件传输功能所使用的端口 TCP 3389端口:Microsoft RDP 微软远程桌面使用的端口 TCP 5631端口:Symantec pcAnywhere 远程控制数据传输时使用的端口 UDP 5632端口:Symantec pcAnywhere 主控端扫描被控端时使用的端口 TCP 5000端口:MS SQL Server使用的端口 UDP 8000端口:腾讯QQ 26、linux常用的命令 top linux进程实时监控 ps 在Linux中是查看进程的命令。ps查看正处于Running的进程 mv 为文件或目录改名或将文件由一个目录移入另一个目录中。 find 查找文件 df 可显示所有文件系统对i节点和磁盘块的使用情况。 cat 打印文件类容 chmod 变更文件或目录的权限 chgrp 文件或目录的权限的掌控以拥有者及所诉群组来管理。可以使用chgrp指令取变更文件与目录所属群组 grep 是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。 wc 为统计指定文件中的字节数、字数、行数,并将统计结果显示输出 27、对于大流量的网站,您采用什么样的方法来解决访问量问题 首先,确认服务器硬件是否足够支持当前的流量 其次,优化数据库访问。 第三,禁止外部的盗链。 第四,控制大文件的下载。 第五,使用不同主机分流主要流量 第六,使用流量分析统计软件 28、$_SERVER常用的字段 $_SERVER['PHP_SELF'] #当前正在执行脚本的文件名 $_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称 $_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT” $_SERVER['QUERY_STRING'] #查询(query)的字符串 $_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容 $_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址 $_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址 $_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名 $_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名 $_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用 $_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html” 29、安装php扩展 进入扩展的目录 phpize命令得到configure文件 ./configure --with-php-config=/usr/local/php/bin/php-config make & make install 在php.ini中加入扩展名称.so 重启web服务器(nginx/apache) 30、php-fpm与nginx PHP-FPM也是一个第三方的FastCGI进程管理器,它是作为PHP的一个补丁来开发的,在安装的时候也需要和PHP源码一起编译,也就是说PHP-FPM被编译到PHP内核中,因此在处理性能方面更加优秀;同时它在处理高并发方面也比spawn-fcgi引擎好很多,因此,推荐Nginx+PHP/PHP-FPM这个组合对PHP进行解析。 FastCGI 的主要优点是把动态语言和HTTP Server分离开来,所以Nginx与PHP/PHP-FPM经常被部署在不同的服务器上,以分担前端Nginx服务器的压力,使Nginx专一处理静态请求和转发动态请求,而PHP/PHP-FPM服务器专一解析PHP动态请求 #fastcgi FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等,同时,FastCGI也被许多脚本语言所支持,其中就有PHP。 FastCGI是从CGI发展改进而来的。传统CGI接口方式的主要缺点是性能很差,因为每次HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后结果被返回给HTTP服务器。这在处理高并发访问时,几乎是不可用的。另外传统的CGI接口方式安全性也很差,现在已经很少被使用了。 FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。 Nginx+FastCGI运行原理 Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket,(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接纳到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端,这就是Nginx+FastCGI的整个运作过程。 31、ajax全称“Asynchronous Javascript And XML”(异步JavaScript和XML)

小川游鱼 2019-12-02 01:41:29 0 浏览量 回答数 0

回答

在校生要找到好工作,主要靠几个光环,学校光环、竞赛光环、项目光环、实习光环。其中项目经验尤为重要。有些同学就有疑问了: “我校招没offer,没有项目经验,是不是要报个培训班?” “我转行计算机,是不是应该报个班?” “我也想自学,可怎么学啊,选哪个方向啊?” 对于有些同学,当我还在想办法劝他自学时,给我贴出了培训班的广告词,真可谓,人有多大胆,口号就有多不要脸: “0基础入学,三个月包就业” “毕业月入不过万,不收学费” “从前是你找工作,接下来是工作找你” 当我推荐某些同学去培训时,又给咔咔咔亮出了几个帖子,说培训出来的受歧视啊、有些同学培训出来还是找不到工作啊,等等。 其实,选择自学还是培训是看自身情况而定,无论选择自学还是培训,都只是入门的一种手段,各有优劣势,本文就详细说说自学/培训怎么选,选择以后怎么办,记得帮我点赞哦。   目录: 自学还是培训,怎么选? 自学怎么学? 培训班到底在培训什么? 有些企业歧视培训班学员,培训班的问题到底出在哪? 一些建议 一、自学还是培训,怎么选? 无论你是什么学历、有没有计算机基础,这些都不是决定你适合自学的条件,具备如下三个条件的人都可以选择自学: (1)、时间充足 如果说从零基础靠自学达到找工作的水平,需要多久呢?我觉得至少一年,有的人可能需要两年。所以,如果你是大一、大二、大三的学生,你还有时间,可以选择自学。如果你是已经工作的,想转行计算机,可以边工作边学习,这个过程会比较辛苦,但也不是绝对不可行。 对于大四的同学,以就业为导向,建议你去培训。不可否认,培训是最快入门的方式,对于时间不足的同学而言,培训是最优解。同样地,如果你是已工作的,不存在财务压力,我同样建议你去培训,工作后的时间很珍贵,比不上在校期间有大把时间可以浪费,如果做好了必转的决心,以最快速度转行才是最优解。 (2)、自控力强 能管得住自己,自己定的目标能想尽一切办法实现的同学,真不多,能占人群中1/4已经不错了。 有些人学了半小时就会累,休息一会,就成这样: 我见过太多的半途而废的同学,也见过太多自己安慰自己式的学习方式,但就业就是一个试金石,你这段时间的努力有没有回报,去找工作的时候,就水落石出。 如果在自律这方面不太行的话,可以看下这篇文章,《启舰:你是怎么变自律的?》,找到自已的驱动器,完成自己的梦想。 (3)、具备高中以上学历 计算机本身是数学家发明的,或多或少会用到一些基本的数学知识、经常用到的很多算法都是数学知识的延伸,没有基本的数学功底,自学确实很难。 至于英语阅读能力还好说,只要会用有道词典,不会的去搜去看,总会读懂的,而且入门级的文献和视频中文版的资料已足够你入门,英语应该不是太大的问题。 如果你这三点都满足,恭喜你,你具有了自学的基础,可以选择自学。 二、自学怎么学? 1、选定一个方向 首先,我们选择方向的目的是什么?不就是为了找份工作吗?那直接到招聘类网站去搜下相关的岗位数量及要求不就好了,哪个数量多,自己也喜欢,那就选这个即可。 其次,如果是大三、大四即将毕业的同学,想知道最近哪个岗位好找工作的话。还可以看看很多培训机构的培训内容,现在很多培训机构都声称保就业,真的以为,培训几个月能培训出朵花来吗?不可能的,编程是个需要长期训练的活,几个月的培训,仅是入门而已,入门的水平能保证找份工作,就靠的是这个岗位门槛低,需求大,好找工作。 如果实在不知道选什么,我帮你找几个方向:python、java后端、Html5就业岗位都挺多,就业门槛低,相对好就业,如果也有其它方向推荐,大家可以留言。 2、找到几套视频教材 在入门时强烈不建议跟着书学 第一,不一定能看得懂 第二,书本的知识不成体系,入门有入门的书,进阶有进阶的书,实战有实战的书,需要自己去选择,本身就不是一件易事。 第三,视频可以看到老师的操作,而书本全靠自己摸 现在某某培训班的入门、进阶、实战的系列视频不要太好找,找到这么两套视频,对比着看,或者跟着一套视频深入看,来得更容易。人家培训班安排好的路线跟着学,不懂的自己搜,就已经排除了自已给自己安排路线的难点,况且人家本身就是面向就业的,培训出来的同学能保就业,只要你能跟着学通学会,自然找到工作也不是问题。 我精心整理了计算机各个方向的从入门、进阶、实战的视频课程和电子书,都是技术学习路上必备的经验,跟着视频学习是进步最快的,而且所有课程都有源码,直接跟着去学!!! 只要关注微信公众号【启舰杂谈】后回复你所需方向的关键字即可,比如『Android』、『java』、『ReactNative』、『H5』、『javaweb』、『面试』、『机器学习』、『web前端』、『设计模式』等关键字获取对应资料。(所有资料免费送,转发宣传靠大家自愿) 视频内容非常多,总共2184G、一千六百多册电子书,九百多套视频教程,涉及43个方向。我整理了很长时间,有些资料是靠买的,希望大家能最快的提升自己。帮我点个赞吧。 启舰:全网2184G计算机各方向视频教程/电子书汇总(持续更新中)​   3、自学,除了知识,你还能学到什么? 自学的缺点很明显: 第一:速度慢,所有进度完全靠自己把控,没有氛围 第二:遇到问题需要自己解决,无人请教 那优点恰恰是从这些缺点中磨练出来的,进度靠自己把握,完全磨练了你的意志力。而所有问题靠自己解决,恰恰培养了你的解决问题的能力。 而这些能力都是培训班教不出来的、无法速成的。而这些能力却是真正的开发高手所必备的 问题定义、分析与设计阶段,这是最需要智商、创造力和经验的阶段,真正的开发高手,就是在这一阶段体现出远超普通人的水平,而在这一阶段所需要的能力,对不起,培训班教不出来,也无法速成,只能靠人自己的努力,慢慢地培养和增强。 4、自学建议 (1)、多做笔记、多复习 刚开始学习时,很难,真的很难。很多东西听不懂,很多东西需要自己搜,自己定的进度很可能完不成。 没关系,坚持下去,都是这么过来的。我刚开始自学的时候,也是无数次想死的冲动…… 学会做笔记,把自己学到的东西及时记下来,形成目录,在后面用到的时候,根据笔记再去看一遍,刚开始经常会出现,听得懂,跟着学会,自己弄就不会的现象。这都是正常的,技术本就是个熟能生巧的过程。 多动手,多总结,就慢慢熟练了。 (2)、多写代码!听得懂、看得懂,并没什么用 入门级知识,本就是语法和框架的熟悉过程,说到底就是工具的使用方法熟悉的过程。既然是工具,那就必然要多用。熟能生巧,指的是用的熟。很多同学看的懂,听的会,自己一下手就问题百出,就是练的少! (3)、听不懂,搜一下,再不懂就放过 刚学的时候很多概念听不懂,没关系,自己搜一下,能理解了就理解,理解不了就算。听一遍就行,学到后面的时候,你就懂些了回头,再看看那些知识,基本上你都懂了。 (4)、多写注释 刚开始的时候,很多逻辑弄不懂,没关系,自己把代码拆解,并对其加以注释,这样,你在反过来再看这些代码时,能很快弄懂它的逻辑。你要知道,你后面学习时还是会碰到这些知识的,而在只看一遍的情况下是不可能记得住的,到时候,你还是会返回来复习这些知识的。 增加注释,看起来浪费时间,其实是整理代码逻辑的过程。浑浑噩噩敲出来的代码,自己都不明白什么意思的话,其实相当于没有真正学会。 三、培训班到底在培训什么? 去培训的主要原因,说到底还是因为自己啥都不会。但不会与不会间是有区别的。 对于科班出身的,上学又好好学了的同学,虽然他们没有系统的编程知识,没有项目经验,但他们有计算机基础,他懂得操作系统原理、数据结构与算法等原理性知识。 而对于跨专业和在玩了四年的同学而言,那才是真正的零基础。 而对于培训机构而言,它的责任就是让你实现从0到1的入门过程,而有经验的老鸟都知道,编程入门仅仅是知识的堆积,并没有什么技巧性可言。所有的语法和框架运用,简单来说,就是学会编程套路,学习工具使用。 而培训机构的责任,就是把这些套路教给你。只要你不太笨,经过几个月的强化训练,大部分人都能学得会。 所以,培训班教你的就是工具的使用,目的,就是以最快的速度塞给你,助你找到工作。 四、有些企业歧视培训班学员,培训班的问题到底出在哪? 培训机构有着熟练的授课体系,老师手把手答疑,让你在学习路上没有一丁点的思考时间,为的就是以最快的速度让你达标,好结课,开始下一波培训。 1、问题就出在速度上。 认知科学的研究成果表明,知识的消化与吸收,职业技能的学习与精通,本质上是在大脑神经元之间建立连接,重塑大脑结构的过程,这个过程的时间可以缩短,但不能无限地缩短。另外,不同的人,拥有不同的背景和基础,在学习与掌握相同的知识与职业技能时,所花的时间是不一样的。 而培训机构才不管这些,他的目的就是挣钱,以最快的速度挣钱,能在三天内把所有内容塞给你绝不用四天,只要最终能糊弄住面试官,让学员找到一份工作就可以了。 所以,必然会出现下面的现象: 对于原来有一些基础的,学习能力较强的同学,在学习之前已经有较扎实的基础,所以在培训期间能够自己构建成技术体系,知识吸收相对较好: 而另一些学员,则会出现消化不良的情况: 2、培训后遗症 对于软件开发而言,所有的软件开发都大致分为两个阶段: 1、分析、定义、设计阶段。这个阶段是需要有解决问题、分析问题的能力。而这个能力培训班培训不出来,只能是慢慢增强。 2、语法、工具的使用,将设计的内容实现出来。这一块就比较机械了,工具嘛,学一学都能会,培训班在这一块的效率是很高效的,它们多半能在较短的时间内,教会学员特定编程语言(比如Python)特定工具(比如Git)与特定技术的使用(比如Spring MVC),并且传授给他们一些开发的“套路”(比如分层架构与设计模式),从而将学员成功地培养成为一个能够“搬砖”的软件工人,即初级程序员。 培训班一般都会选择门槛低、就业岗位多的方向进行培训,对于这类岗位,人才缺口大,只要能直接上手写代码的初级程序员,都很容易找到一份工作。这也就是为什么培训班多半会收学生五位数的学费,而学生也愿意支付的根本原因。 (1)、解决问题能力差,动不动就得人教 经过几个月饭来张口、衣来伸手的填鸭式集训,有些人在工作后,却依然认为,当他遇到问题时,从来不想着自己搜搜资料解决,而是依赖同事帮他答疑! 自学能力差、解决问题能力差,是很多人找到了工作,过不了试用期的根本原因。 (2)、培训效果立竿见影,却又很快遗忘 任何的知识都是一样,短时间内填鸭式学到的知识,在一段时间不用后,就会遗忘。这就是有些同学刚从培训班出来时,能找到份工作,当学到的东西在工作中几个月用不到时,就很快忘记,总觉得自己还是啥都不会的原因。 永远要记住:学历不行靠实力,实力不行靠态度!!! 当我们初入职场,尽心尽责地把自己的工作做完做好的同时,千万不要忘记像海绵一样,以最快的速度给自己充水。 像培训完的同学,在校期间已经做了很多的笔记,工作之余,多复习,重新练,利用时间将它理解,真正内化为自己的本领。 对于自学的同学,多找进阶性书籍和视频去看,以最快的速度提升自己。 文末我整理了计算机各个方向的从入门、进阶、实战的视频课程和电子书,都是技术学习路上必备的经验,跟着视频学习是进步最快的,而且所有课程都有源码,直接跟着去学!!! 五、一些建议 1、非科班同学建议 对于非科班转行计算机的同学,有太多的知识需要补足,如果你靠的是自学,需要强有力的自律能力,只要时间还够,是可以靠自学的,在跟着视频学的时候,哪里听不懂及时去搜相关的资料去补足。 刚开始自学时,即便是科班出身也是有想死的冲动的,大家都一样。我也是靠自学过来的,很多的东西不会,很多的东西听不懂。没关系,多做笔试,多搜资料,把不会的弄会,你会发现,学习起来越来越容易。 所有的困难只不过是纸老虎,坚持过去就成功了。 如果你是通过培训找到了一份工作,你需要比别人更努力补充计算机知识,基础知识的缺乏,会使你很难在这条路上走很远,所有的大神,都是自学能力很强的人,你想,你也可以。 2、所有开发方向都必须从C++开始? 经常会有要校生问我:我要做H5开发,是不是要先学C++? 其实,各个语言之间是没有任何关联的,完全都是有各自的语法体系和开发工具的,简单来讲,他们都是不同类型的工具。 你学会一种工具,只会对另一种类似的工具更容易上手,而不是完全不用学。所以,想学哪个方向,直接去学就行了,没必须先从C++入手迂回一下,纯属浪费时间。 但,如果你还在上学,现在正在学C++,那我还是建议你好好学,必须C语言语法更接近低层编译器原理,学会了它,对理解低层分配、释放、编译机制都是很有用的,但就以工作为导向而言,如果你不从事C++相关工作,是没必要学的。 3、培训出来人人工资过万? 有个男生非常沮丧的找我,自己是专科毕业,培训完,小公司不想进,大点的公司进不去,给的工资也不高,问我怎么办? 上面我们已经讲到,对于不同程度的同学,在培训出来的结果是不一样的,你要分清,你培训完的情况是属于这种: 还是这种? 对于没有名校光环的同学,建议以先就业为主。 别看培训班招你的时候给你洗脑,培训完人人过万,但能不能过万,最终靠的是自己,而不是培训班。 认请自己的情况,可以先就业,再优化自己履历,而进步步高升。 4、建议不要暴露自己的培训经历 你百度、知乎搜一下,遍地的培训歧视,很多公司根本不要培训出来的同学. 业界对培训有偏见,因为写代码是一个逐渐学习、熟练的过程,经过几个月集中的培训,虽然看起来什么都接触到了,但真正能内化为自己知识的部分其实不多。在工作中并不能熟练运用,仅是入门水平而已。 而且大家普遍认为参加培训的主要原因是因为,大学中没好好学,临近毕业了,催熟一把。不然,谁会花这几万块钱呢?对普通家庭而言,其实也并不是个小数目了。 有一个外包公司的朋友,技术总监,招人时培训公司出来都不要,原因就是干活能力不行。当然这仅代表个例,但大家需要注意的是,业界并不认为培训是一件光彩的事,千万不要搞错了!!! 5、培训班防骗三十六计 现在太多的培训机构,一个个把自己吹的天花乱坠,我也建议过小伙伴去培训,但小孩子交完钱培训一个月就退费了,深感自己好心做了坏事,这里建议大家培训市场,鱼龙混杂,一定要提前做好防骗准备。 谎言之所以真实是因为年青的心太不甘寂寞,太急于求成! 从网上找了,培训班防骗三十六计,供大家参考: “借刀杀人”:培训班间竞争激烈,彼此勾心斗角,正好为我所用。去培训班甲问乙如何,到培训班乙打听甲。Ha.Ha..,狗咬狗开始了,一时间内幕迭报:乙设备不全,很多实验不能做;甲的那个号称CCIE的老师只过了笔试,没过实验室,假的! “声东击西”:与甲约好星期六考察学校,结果星期X跑去(1=< X <= 5)。   “你怎么来了?”   “我星期六有事,所以提前来看看……” “抛砖引玉”:有时候,拿不定注意或者培训班在外地,实地考察有难度,何不到论坛发个帖子征求意见,要是能得到已经培训过的前辈的释疑,那你绝对是不虚此帖了! “假痴不癫”:有时候你可能偶然拥有一些内幕消息,不如试试他们的诚实度。   “听说你们的教师是CCIE!”   “那当然,技术首屈一指,……”   此时此刻,看着乙那得意样样的小样,不知是好笑,还是可气。不过记住:一个没有诚信的公司是什么都干的出来的! “反间计”:一个卑鄙的培训班后面一般都有一个卑鄙的流氓大亨,他不仅千方百计的从学员那里榨取钱财,对自己的手下也不会心慈手软,本着人们内部矛盾的原则发展一个或多个间谍。 “走为上计”:经过一番打探,知道他们都不是东西,还犹豫什么?宁缺毋滥,走人! 最后,如论怎么选,自终也只是入门阶段,为了找到一份工作。对于初入职场的你们,给一条最终建议:学校不行靠实力,实力不行靠态度。记得帮我点赞哦。 ———————————————— 版权声明:本文为CSDN博主「启舰」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/harvic880925/article/details/103413853

问问小秘 2020-01-07 10:55:15 0 浏览量 回答数 0

回答

. 在编写一个类时,如果该类中的代码可能运行与多线程环境下,就要考虑同步问题了。 会同时被多个线程访问的资源,就是竞争资源,也称为竞争条件。对于多线程共享的资源我们必须进行同步,以避免一个线程的改动被另一个线程所覆盖。 synchronized 关键字有两种作用域: 1> 某个对象实例内,synchronized aMethod(){}关键字可以防止多个线程访问对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法. 2> 是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。 synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法; Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。      一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。      二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。      三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。      四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部   分的访问都被暂时阻塞。      五、以上规则对其它对象锁同样适用. 2. synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。   synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:   synchronized void accessVal(int newVal);   synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能 执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行 状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有 一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized) 。  在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成 员变量的访问。  synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可 以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供 了更好的解决办法,那就是 synchronized 块。   synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:  synchronized(syncObject) {   //允许访问控制的代码  }  synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机 制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。  对synchronized(this)的一些理解 一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线 程必须等待当前线程执行完这个代码块以后才能执行该代码块。  二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized (this)同步代码块。  三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this) 同步代码块的访问将被阻塞。  四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个 object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。  五、以上规则对其它对象锁同样适用 3.打个比方:一个object就像一个大房子,大门永远打开。房子里有 很多房间(也就是方法)。 这些房间有上锁的(synchronized方法), 和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把钥匙可以打开所有上锁的房间。 另外我把所有想调用该对象方法的线程比喻成想进入这房子某个 房间的人。所有的东西就这么多了,下面我们看看这些东西之间如何作用的。 在此我们先来明确一下我们的前提条件。该对象至少有一个synchronized方法,否则这个key还有啥意义。当然也就不会有我们的这个主题了。 一个人想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明暂时还没有其他人要使用上锁的 房间)。于是他走上去拿到了钥匙 ,并且按照自己 的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会马上把钥匙还回去。即使他要连续使用两间上锁的房间, 中间他也要把钥匙还回去,再取回来。 因此,普通情况下钥匙的使用原则是:“随用随借,用完即还。” 这时其他人可以不受限制的使用那些不上锁的房间,一个人用一间可以,两个人用一间也可以,没限制。但是如果当某个人想要进入上锁的房 间,他就要跑到大门口去看看了。有钥匙当然拿了就走,没有的话,就只能等了。 要是很多人在等这把钥匙,等钥匙还回来以后,谁会优先得到钥匙?Not guaranteed。象前面例子里那个想连续使用两个上锁房间的家伙,他 中间还钥匙的时候如果还有其他人在等钥匙,那么没有任何保证这家伙能再次拿到。 (JAVA规范在很多地方都明确说明不保证,象 Thread.sleep()休息后多久会返回运行,相同优先权的线程那个首先被执行,当要访问对象的锁被 释放后处于等待池的多个线程哪个会优先得 到,等等。我想最终的决定权是在JVM,之所以不保证,就是因为JVM在做出上述决定的时候,绝不是简简单单根据 一个条件来做出判断,而是 根据很多条。而由于判断条件太多,如果说出来可能会影响JAVA的推广,也可能是因为知识产权保护的原因吧。SUN给了个不保证 就混过去了 。无可厚非。但我相信这些不确定,并非完全不确定。因为计算机这东西本身就是按指令运行的。即使看起来很随机的现象,其实都是有规律 可寻。学过 计算机的都知道,计算机里随机数的学名是伪随机数,是人运用一定的方法写出来的,看上去随机罢了。另外,或许是因为要想弄 的确定太费事,也没多大意义,所 以不确定就不确定了吧。) 再来看看同步代码块。和同步方法有小小的不同。 1.从尺寸上讲,同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间。 2.同步代码块还可以人为的指定获得某个其它对象的key。就像是指定用哪一把钥匙才能开这个屏风的锁,你可以用本房的钥匙;你也可以指定 用另一个房子的钥匙才能开,这样的话,你要跑到另一栋房子那儿把那个钥匙拿来,并用那个房子的钥匙来打开这个房子的带锁的屏风。          记住你获得的那另一栋房子的钥匙,并不影响其他人进入那栋房子没有锁的房间。          为什么要使用同步代码块呢?我想应该是这样的:首先对程序来讲同步的部分很影响运行效率,而一个方法通常是先创建一些局部变 量,再对这些变量做一些 操作,如运算,显示等等;而同步所覆盖的代码越多,对效率的影响就越严重。因此我们通常尽量缩小其影响范围。 如何做?同步代码块。我们只把一个方法中该同 步的地方同步,比如运算。          另外,同步代码块可以指定钥匙这一特点有个额外的好处,是可以在一定时期内霸占某个对象的key。还记得前面说过普通情况下钥 匙的使用原则吗。现在不是普通情况了。你所取得的那把钥匙不是永远不还,而是在退出同步代码块时才还。           还用前面那个想连续用两个上锁房间的家伙打比方。怎样才能在用完一间以后,继续使用另一间呢。用同步代码块吧。先创建另外 一个线程,做一个同步代码 块,把那个代码块的锁指向这个房子的钥匙。然后启动那个线程。只要你能在进入那个代码块时抓到这房子的钥匙 ,你就可以一直保留到退出那个代码块。也就是说 你甚至可以对本房内所有上锁的房间遍历,甚至再sleep(10601000),而房门口却还有 1000个线程在等这把钥匙呢。很过瘾吧。           在此对sleep()方法和钥匙的关联性讲一下。一个线程在拿到key后,且没有完成同步的内容时,如果被强制sleep()了,那key还一 直在 它那儿。直到它再次运行,做完所有同步内容,才会归还key。记住,那家伙只是干活干累了,去休息一下,他并没干完他要干的事。为 了避免别人进入那个房间 把里面搞的一团糟,即使在睡觉的时候他也要把那唯一的钥匙戴在身上。           最后,也许有人会问,为什么要一把钥匙通开,而不是一个钥匙一个门呢?我想这纯粹是因为复杂性问题。一个钥匙一个门当然更 安全,但是会牵扯好多问题。钥匙 的产生,保管,获得,归还等等。其复杂性有可能随同步方法的增加呈几何级数增加,严重影响效率。这也 算是一个权衡的问题吧。为了增加一点点安全性,导致效 率大大降低,是多么不可取啊。 synchronized的一个简单例子 public class TextThread { public static void main(String[] args) {    TxtThread tt = new TxtThread();    new Thread(tt).start();    new Thread(tt).start();    new Thread(tt).start();    new Thread(tt).start(); } } class TxtThread implements Runnable { int num = 100; String str = new String(); public void run() {    synchronized (str) {     while (num > 0) {      try {       Thread.sleep(1);      } catch (Exception e) {       e.getMessage();      }      System.out.println(Thread.currentThread().getName()        + "this is " + num--);     }    } } } 上面的例子中为了制造一个时间差,也就是出错的机会,使用了Thread.sleep(10) Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键字就可以轻松地解决多线程共享数据同步问题。到底如 何?――还得对synchronized关键字的作用进行深入了解才可定论。 总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类, synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。 在进一步阐述之前,我们需要明确几点: A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其 他线程的对象访问。 B.每个对象只有一个锁(lock)与之相关联。 C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。 接着来讨论synchronized用到不同地方对代码产生的影响: 假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。 1. 把synchronized当作函数修饰符时,示例代码如下: Public synchronized void methodAAA() { //…. } 这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中 执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了 synchronized关键字的方法。 上边的示例代码等同于如下代码: public void methodAAA() { synchronized (this)      // (1) {        //….. } } (1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个 拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造 成数据混乱:( 2.同步块,示例代码如下: public void method3(SomeObject so) {     synchronized(so)     {        //…..     } } 这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明 确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁: class Foo implements Runnable {         private byte[] lock = new byte[0]; // 特殊的instance变量         Public void methodA()         {            synchronized(lock) { //… }         }         //….. } 注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。 3.将synchronized作用于static 函数,示例代码如下: Class Foo {     public synchronized static void methodAAA()   // 同步的static 函数     {         //….     }     public void methodBBB()     {        synchronized(Foo.class)   // class literal(类名称字面常量)     } }    代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这 个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。 记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的 目的。P1指的是由Foo类产生的对象。 可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj 在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。 小结如下: 搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。 还有一些技巧可以让我们对共享资源的同步访问更加安全: 1. 定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以 绕过同步方法的控制而直接取得它,并改动它。这也是JavaBean的标准实现方式之一。 2. 如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象 的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就需要将get方法也加上synchronized同步,并 且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了 作者:hanwei_java 来源:CSDN 原文:https://blog.csdn.net/hanwei_java/article/details/79738614 版权声明:本文为博主原创文章,转载请附上博文链接!

auto_answer 2019-12-02 01:50:26 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 阿里云双十一主会场 阿里云双十一新人会场 1024程序员加油包 阿里云双十一拼团会场 场景化解决方案 阿里云双十一直播大厅