• 关于

    直接赋值语句出问题什么情况

    的搜索结果

回答

final   在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)。下面将对其详细介绍。 1.1 修饰类   当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰,但要注意: final类中所有的成员方法都会隐式的定义为final方法。 1.2 修饰方法 使用final方法的原因主要有两个:   (1) 把方法锁定,以防止继承类对其进行更改。   (2) 效率,在早期的java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行这些优化了。 final方法意味着“最后的、最终的”含义,即此方法不能被重写。 注意:若父类中final方法的访问权限为private,将导致子类中不能直接继承该方法,因此,此时可以在子类中定义相同方法名的函数,此时不会与重写final的矛盾,而是在子类中重新地定义了新方法。复制代码 class A{ private final void getName(){ } } public class B extends A{ public void getName(){ } public static void main(String[]args){ System.out.println("OK"); } } 复制代码    1.3 修饰变量   final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。类似于C++中的const。   当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。    final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。   当函数的参数类型声明为final时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。       在java中,String被设计成final类,那为什么平时使用时,String的值可以被改变呢?   字符串常量池是java堆内存中一个特殊的存储区域,当我们建立一个String对象时,假设常量池不存在该字符串,则创建一个,若存在则直接引用已经存在的字符串。当我们对String对象值改变的时候,例如 String a="A"; a="B" 。a是String对象的一个引用(我们这里所说的String对象其实是指字符串常量),当a=“B”执行时,并不是原本String对象("A")发生改变,而是创建一个新的对象("B"),令a引用它。 finally   finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。(×)(这句话其实存在一定的问题)   很多人都认为finally语句块一定会执行,但真的是这样么?答案是否定的,例如下面这个例子:      当我们去掉注释的三行语句,执行结果为:      为什么在以上两种情况下都没有执行finally语句呢,说明什么问题?   只有与finally对应的try语句块得到执行的情况下,finally语句块才会执行。以上两种情况在执行try语句块之前已经返回或抛出异常,所以try对应的finally语句并没有执行。   但是,在某些情况下,即使try语句执行了,finally语句也不一定执行。例如以下情况:      finally 语句块还是没有执行,为什么呢?因为我们在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行。那有人说了,在一般的 Java 应用中基本上是不会调用这个 System.exit(0) 方法的。OK !没有问题,我们不调用 System.exit(0) 这个方法,那么 finally 语句块就一定会执行吗?   再一次让大家失望了,答案还是否定的。当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。可能有人认为死机、断电这些理由有些强词夺理,没有关系,我们只是为了说明这个问题。 易错点   在try-catch-finally语句中执行return语句。我们看如下代码:      答案:4,4,4 。 为什么呢?   首先finally语句在改代码中一定会执行,从运行结果来看,每次return的结果都是4(即finally语句),仿佛其他return语句被屏蔽掉了。   事实也确实如此,因为finally用法特殊,所以会撤销之前的return语句,继续执行最后的finally块中的代码。    finalize     finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。 特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。   使用finalize还需要注意一个事,调用super.finalize();   一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。 所以,推荐不要使用finalize()方法,它跟析构函数不一样。

wangccsy 2019-12-02 01:48:34 0 浏览量 回答数 0

回答

1.字符串转义序列转义字符 描述(在行尾时) 续行符\ 反斜杠符号' 单引号" 双引号a 响铃b 退格(Backspace)e 转义000 空n 换行v 纵向制表符t 横向制表符r 回车f 换页oyy 八进制数yy代表的字符,例如:o12代表换行xyy 十进制数yy代表的字符,例如:x0a代表换行other 其它的字符以普通格式输出 2.字符串格式化 3.操作符 一、算术运算符 注意: 双斜杠 // 除法总是向下取整。 从符点数到整数的转换可能会舍入也可能截断,建议使用math.floor()和math.ceil()明确定义的转换。 Python定义pow(0, 0)和0 ** 0等于1。 二、比较运算符 运算符 描述< 小于<= 小于或等于 大于= 大于或等于== 等于 != 不等于is 判断两个标识符是不是引用自一个对象is not 判断两个标识符是不是引用自不同对象注意: 八个比较运算符优先级相同。 Python允许x < y <= z这样的链式比较,它相当于x < y and y <= z。 复数不能进行大小比较,只能比较是否相等。 三、逻辑运算符 运算符 描述 备注x or y if x is false, then y, elsex x andy if x is false, then x, elsey not x if x is false, then True,elseFalse 注意: or是个短路运算符,它只有在第一个运算数为False时才会计算第二个运算数的值。 and也是个短路运算符,它只有在第一个运算数为True时才会计算第二个运算数的值。 not的优先级比其他类型的运算符低,所以not a == b相当于not (a == b),而 a == not b是错误的。 四、位运算符 运算符 描述 备注x | y 按位或运算符 x ^ y 按位异或运算符 x & y 按位与运算符 x << n 左移动运算符 x >> n 右移动运算符 ~x 按位取反运算符 五、赋值运算符 复合赋值运算符与算术运算符是一一对应的: 六、成员运算符 Python提供了成员运算符,测试一个元素是否在一个序列(Sequence)中。 运算符 描述in 如果在指定的序列中找到值返回True,否则返回False。not in 如果在指定的序列中没有找到值返回True,否则返回False。 4.关键字总结 Python中的关键字包括如下: and del from not while as elif global or with assert else if pass yield break except import print class exec in raise continue finally is return def for lambda try你想看看有哪些关键字?OK,打开一个终端,就像这样~ long@zhouyl:~$ pythonPython 2.7.3 (default, Jan 2 2013, 16:53:07) [GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information. import keywordkeyword.kwlist ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'] ============================== 华丽的 正文分隔符 ======================================== 看到这些关键字你还能记得多少?你不妨自己一个一个对照想想它的用法,下面是我总结的,我根据前面的学习笔记将上述关键字分为以下几类: 1.判断、循环 对于Python的循环及判断主要包括这些关键字: if elif else for while break continue and or is not in 这几个关键字在前面介绍 if 语法、while语法、for语法以及and...or语法中已有介绍,下面再一笔带过: 1.1 if 语法 if语法与C语言、shell脚本之下的非常类似,最大的区别就是冒号以及严格的缩进,当然这两点也是Python区别于其他语言的地方: if condition1: do something elif condition2: do another thing else: also do something 1.2 while 语法 Python的while语法区别于C、shell下的while除了冒号及缩进之外,还有一点就是while可以携带一个可选的else语句: while condition: do something else: do something 注:else语句是可选的,但是使用while语句时一定要注意判断语句可以跳出! 1.3 for 语法 与while类似,Python的for循环也包括一个可选的else语句(跳出for循环时执行,但是如果是从break语句跳出则不执行else语句块中的代码!),而且for 加上 关键字in就组成了最常见的列表解析用法(以后会写个专门的博客)。 下面是for的一般用法: for i in range(1,10,2): do something if condition: break else: do something for的列表解析用法: for items in list: print items 1.4 and...or 语法 Python的and/or操作与其他语言不同的是它的返回值是参与判断的两个值之一,所以我们可以通过这个特性来实现Python下的 a ? b : c ! 有C语言基础的知道 “ a ? b : c ! ” 语法是判断 a,如果正确则执行b,否则执行 c! 而Python下我们可以这么用:“ a and b or c ”(此方法中必须保证b必须是True值),python自左向右执行此句,先判断a and b :如果a是True值,a and b语句仍需要执行b,而此时b是True值!所以a and b的值是b,而此时a and b or c就变成了b or c,因b是True值,所以b or c的结果也是b;如果a是False值,a and b语句的结果就是a,此时 a and b or c就转化为a or c,因为此时a是 False值,所以不管c是True 还是Flase,a or c的结果就是c!!!捋通逻辑的话,a and b or c 是不是就是Python下的a ? b : c ! 用法? 1.5 is ,not is 和 is not 是Python下判断同一性的关键字,通常用来判断 是 True 、False或者None(Python下的NULL)! 比如 if alue is True : ... (不记得本节的童鞋罚复习:python 学习笔记 2 -- 判断语句) 2.函数、模块、类 对于Python的函数及模块主要包括这些关键字: from import as def pass lambda return class 那么你还能记得它们么?下面简单介绍一下: 2.1 模块 Python的编程通常大量使用标准库中的模块,使用方法就是使用import 、from以及as 关键字。 比如: import sys # 导入sys模块 from sys import argv # 从sys模块中导入argv ,这个在前面介绍脚本传参数时使用到 import cPickle as p # 将cPickle模块导入并在此将它简单命名为p,此后直接可以使用p替代cPickle模块原名,这个在介绍文件输入输出时的存储器中使用到 2.2 函数 Python中定义函数时使用到def关键字,如果你当前不想写入真实的函数操作,可以使用pass关键字指代不做任何操作: def JustAFunction: pass 当然,在需要给函数返回值时就用到了return关键字,这里简单提一下Python下的函数返回值可以是多个(接收返回值时用相应数量的变量接收!)! 此外Python下有个神奇的Lambda函数,它允许你定义单行的最小函数,这是从Lisp中借用来的,可以用在任何需要函数的地方。比如: g = lambda x : x*2 # 定义一个Lambda函数用来计算参数的2倍并返回! print g(2) # 使用时使用lambda函数返回的变量作为这个函数的函数名,括号中带入相应参数即可! (不记得本节的童鞋罚复习:python 学习笔记 4 -- 函数篇) 3.异常 对于Python的异常主要包括这些关键字: try except finally raise 异常这一节还是比较简单的,将可能出现的异常放在 try: 后面的语句块中,使用except关键字捕获一定的异常并在接下来的语句块中做相应操作,而finally中接的是无论出现什么异常总在执行最后做finally: 后面的语句块(比如关闭文件等必要的操作!) raise关键字是在一定的情况下引发异常,通常结合自定义的异常类型使用。 (不记得本节的童鞋罚复习:python 学习笔记 6 -- 异常处理) 4.其他 上面的三类过后,还剩下这些关键字: print del global with assert yield exec 首先print 在前面的笔记或者任何地方你都能见到,所以还是比较熟悉的,此处就不多介绍了!del 关键字在前面的笔记中已有所涉及,比如删除列表中的某项,我们使用 “ del mylist[0] ” 可能这些剩下来的关键字你比较陌生,所以下面来介绍一下: 4.1.global 关键字 当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是 局部 的。这称为变量的 作用域 。所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开始。 eg. ? 1 2 3 4 5 6 7 8 9 10 11 !/usr/bin/python Filename: func_local.py def func(x): print'x is', x x = 2 print'Changed local x to', x x = 50 func(x) print'x is still', x 运行的结果是这样的:? 1 2 3 4 $ python func_local.py x is 50 # 运行func函数时,先打印x的值,此时带的值是作为参数带入的外部定义的50,所以能正常打印 x=50 Changed local x to 2 # 在func函数中将x赋2,并打印 x is still 50 # 运行完func函数,打印x的值,此时x的值仍然是之前赋给的50,而不是func函数中修改过的2,因为在函数中修改的只是函数内的局部变量 那么为什么我们要在这提到局部变量呢?bingo,聪明的你一下就猜到这个global就是用来定义全局变量的。也就是说如果你想要为一个在函数外定义的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是 全局 的。我们使用global语句完成这一功能。没有global语句,是不可能为定义在函数外的变量赋值的。eg.? 1 2 3 4 5 6 7 8 9 10 11 12 !/usr/bin/python Filename: func_global.py def func(): global x print'x is', x x = 2 print'Changed local x to', x x = 50 func() print'Value of x is', x 运行的结果是这样的:? 1 2 3 4 $ python func_global.py x is 50 Changed global x to 2 Value of x is 2 # global语句被用来声明x是全局的——因此,当我们在函数内把值赋给x的时候,这个变化也反映在我们在主块中使用x的值的时候。 你可以使用同一个global语句指定多个全局变量。例如global x, y, z。 4.2.with 关键字 有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。如果不用with语句,打开一个文件并读文件的代码如下:? 1 2 3 file = open("/tmp/foo.txt") data = file.read() file.close() 当然这样直接打开有两个问题:一是可能忘记关闭文件句柄;二是文件读取数据发生异常,没有进行任何处理。下面是添加上异常处理的版本:? 1 2 3 4 5 file = open("/tmp/foo.txt") try: data = file.read() finally: file.close() 虽然这段代码运行良好,但是太冗余了。这时候就是with一展身手的时候了。除了有更优雅的语法,with还可以很好的处理上下文环境产生的异常。下面是with版本的代码:? 1 2 with open("/tmp/foo.txt") as file: data = file.read() 这看起来充满魔法,但不仅仅是魔法,Python对with的处理还很聪明。基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。with语句的执行逻辑如下:紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。 下面例子可以具体说明with如何工作:? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 !/usr/bin/python with_example01.py classSample: def __enter__(self): print"In __enter__()" return"Foo" def __exit__(self, type, value, trace): print"In __exit__()" def get_sample(): returnSample() with get_sample() as sample: print"sample:", sample 运行代码,输出如下? 1 2 3 4 $python with_example01.py In __enter__() # __enter__()方法被执行 sample: Foo # __enter__()方法返回的值 - 这个例子中是"Foo",赋值给变量'sample',执行代码块,打印变量"sample"的值为"Foo" In __exit__() # __exit__()方法被调用 4.3.assert 关键字 assert语句是一种插入调试断点到程序的一种便捷的方式。assert语句用来声明某个条件是真的,当assert语句失败的时候,会引发一AssertionError,所以结合try...except我们就可以处理这样的异常。 mylist # 此时mylist是有三个元素的列表['a', 'b', 'c']assert len(mylist) is not None # 用assert判断列表不为空,正确无返回assert len(mylist) is None # 用assert判断列表为空 Traceback (most recent call last): File "", line 1, in AssertionError # 引发AssertionError异常 4.4.yield 关键字 我们先看一个示例:? 1 2 3 4 5 6 7 8 def fab(max): n, a, b = 0,0,1 whilen < max: yield b # print b a, b = b, a + b n = n + 1 ''' 使用这个函数:? 1 2 3 4 5 6 7 8 forn in fab(5): ... print n ... 1 1 2 3 5 简单地讲,yield 的作用就是把一个函数变成一个 generator(生成器),带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable(可迭代的)对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 f = fab(5) f.next() 1 f.next() 1 f.next() 2 f.next() 3 f.next() 5 f.next() Traceback (most recent call last): File"", line 1, in StopIteration 当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。 我们可以得出以下结论:一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。 yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。 注:如果看完此段你还未明白yield,没问题,因为yield是初学者的一个难点,那么你下一步需要做的就是……看一看下面参考资料中给的关于yield的博文! 4.5.exec 关键字 官方文档对于exec的解释: "This statement supports dynamic execution of Python code."也就是说使用exec可以动态执行Python代码(也可以是文件)。? 1 2 3 4 5 6 7 8 9 10 11 12 13 longer = "print "Hello World ,my name is longer"" # 比如说我们定义了一个字符串 longer 'print "Hello World ,my name is longer"' exec(longer) # 使用exec 动态执行字符串中的代码 Hello World ,my name is longer exec(sayhi) # 使用exec直接打开文件名(指定sayhi,sayhi.py以及"sayhi.py"都会报一定的错,但是我觉得直接带sayhi报错非常典型) Traceback (most recent call last): File"", line 1, in TypeError: exec: arg 1must be a string, file, or code object # python IDE报错,提示exec的第一个参 数必须是一个字符串、文件或者一个代码对象 f = file("sayhi.py") # 使用file打开sayhi.py并创建f实例 exec(f) # 使用exec直接运行文件描述符f,运行正常!! Hi,thisis [''] script 上述给的例子比较简单,注意例子中exec语句的用法和eval_r(), execfile()是不一样的. exec是一个关键字(要不然我怎么会在这里介绍呢~~~), 而eval_r()和execfile()则是内建函数。更多关于exec的使用请详看引用资料或者Google之 在需要在字符中使用特殊字符时,python用反斜杠()转义字符。 原始字符串 有时我们并不想让转义字符生效,我们只想显示字符串原来的意思,这就要用r和R来定义原始字符串。如: print r’tr’ 实际输出为“tr”。 转义字符 描述 (在行尾时) 续行符 反斜杠符号 ’ 单引号 ” 双引号 a 响铃 b 退格(Backspace) e 转义 000 空 n 换行 v 纵向制表符 t 横向制表符 r 回车 f 换页 oyy 八进制数yy代表的字符,例如:o12代表换行 xyy 十进制数yy代表的字符,例如:x0a代表换行 other 其它的字符以普通格式输出

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

回答

我们先通过一个简单的代码来了解该问题。同步问题我们使用一个简单的结构体 Counter,该结构体包含一个值以及一个方法用来改变这个值: 1 struct Counter { 2 int value; 3 4 void increment(){ 5 ++value; 6 } 7}; 然后启动多个线程来修改结构体的值: 1 int main(){ 2 Counter counter; 3 4 std::vector<std::thread> threads; 5 for(int i = 0; i < 5; ++i){ 6 threads.push_back(std::thread([&counter](){ 7 for(int i = 0; i < 100; ++i){ 8 counter.increment(); 9 } 10 })); 11 } 12 13 for(auto& thread : threads){ 14 thread.join(); 15 } 16 17 std::cout << counter.value << std::endl; 18 19 return 0; 20} 我们启动了5个线程来增加计数器的值,每个线程增加了100次,然后在线程结束时打印计数器的值。但我们运行这个程序的时候,我们是希望它会答应500,但事实不是如此,没人能确切知道程序将打印什么结果,下面是在我机器上运行后打印的数据,而且每次都不同: 442 500 477 400 422 487 问题的原因在于改变计数器值并不是一个原子操作,需要经过下面三个操作才能完成一次计数器的增加:首先读取 value 的值然后将 value 值加1将新的值赋值给 value但你使用单线程来运行这个程序的时候当然没有任何问题,因此程序是顺序执行的,但在多线程环境中就有麻烦了,想象下下面这个执行顺序:Thread 1 : 读取 value, 得到 0, 加 1, 因此 value = 1Thread 2 : 读取 value, 得到 0, 加 1, 因此 value = 1Thread 1 : 将 1 赋值给 value,然后返回 1Thread 2 : 将 1 赋值给 value,然后返回 1这种情况我们称之为多线程的交错执行,也就是说多线程可能在同一个时间点执行相同的语句,尽管只有两个线程,交错的现象也很明显。如果你有更多的线程、更多的操作需要执行,那么这个交错是必然发生的。有很多方法来解决线程交错的问题:信号量 Semaphores原子引用 Atomic referencesMonitorsCondition codesCompare and swap在这篇文章中我们将学习如何使用信号量来解决这个问题。信号量也有很多人称之为互斥量(Mutex),同一个时间只允许一个线程获取一个互斥对象的锁,通过 Mutex 的简单属性就可以用来解决交错的问题。使用 Mutex 让计数器程序是线程安全的 在 C++11 线程库中,互斥量包含在 mutex 头文件中,对应的类是 std::mutex,有两个重要的方法 mutex:lock() 和 unlock() ,从名字上可得知是用来锁对象以及释放锁对象。一旦某个互斥量被锁,那么再次调用 lock() 返回堵塞值得该对象被释放。为了让我们刚才的计数器结构体是线程安全的,我们添加一个 set:mutext 成员,并在每个方法中通过 lock()/unlock() 方法来进行保护: struct Counter { std::mutex mutex; int value; Counter() : value(0) {} void increment(){ mutex.lock(); ++value; mutex.unlock(); } }; 然后我们再次测试这个程序,打印的结果就是 500 了,而且每次都一样。异常和锁 现在让我们来看另外一种情况,想象我们的的计数器有一个减操作,并在值为0的时候抛出异常: struct Counter { int value; Counter() : value(0) {} void increment(){ ++value; } void decrement(){ if(value == 0){ throw "Value cannot be less than 0"; } --value; } }; 然后我们不需要修改类来访问这个结构体,我们创建一个封装器: struct ConcurrentCounter { std::mutex mutex; Counter counter; void increment(){ mutex.lock(); counter.increment(); mutex.unlock(); } void decrement(){ mutex.lock(); counter.decrement(); mutex.unlock(); } }; 大部分时候该封装器运行挺好,但是使用 decrement 方法的时候就会有异常发生。这是一个大问题,一旦异常发生后,unlock 方法就没被调用,导致互斥量一直被占用,然后整个程序就一直处于堵塞状态(死锁),为了解决这个问题我们需要用 try/catch 结构来处理异常情况: void decrement(){ mutex.lock(); try { counter.decrement(); } catch (std::string e){ mutex.unlock(); throw e; } mutex.unlock(); } 这个代码并不难,但看起来很丑,如果你一个函数有 10 个退出点,你就必须为每个退出点调用一次 unlock 方法,或许你可能在某个地方忘掉了 unlock ,那么各种悲剧即将发生,悲剧发生将直接导致程序死锁。接下来我们看如何解决这个问题。自动锁管理 当你需要包含整段的代码(在我们这里是一个方法,也可能是一个循环体或者其他的控制结构),有这么一种好的解决方法可以避免忘记释放锁,那就是 std::lock_guard.这个类是一个简单的智能锁管理器,但创建 std::lock_guard 时,会自动调用互斥量对象的 lock() 方法,当 lock_guard 析构时会自动释放锁,请看下面代码: struct ConcurrentSafeCounter { std::mutex mutex; Counter counter; void increment(){ std::lock_guard<std::mutex> guard(mutex); counter.increment(); } void decrement(){ std::lock_guard<std::mutex> guar(mutex); mutex.unlock(); } };

a123456678 2019-12-02 01:56:44 0 浏览量 回答数 0

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

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

回答

1、了解视频面试的有效交流成分 面试者可先试演盯着摄像头说话,让对方有一种面谈的感觉;增加一些无伤大雅的微动作,比如点头赞同对方;以及找到自己最适合视频说话的语调和语速,这些将会缩小与面试官的距离感。 2、熟悉面试平台的操作流程 可以使用一下自己常用的招聘APP,查找一下平台视频面试流程的详细说明 3、做好个人面试前的准备 天下大事,必作于细。除了对视频面试和面试平台的了解,个人的准备也是事关重要的。 - [1]个人的形象准备。 虽然是线上的视频面试,但还是可以看到彼此,我们都需要做好准备。比如面试官在国外的下午进行视频面试,国内刚好是晚上,如果此时一身家居服的你与面试官视频,对方难以感受到尊重。所以,无论任何时间点,符合面试的正式服装并且穿戴整齐,才能将专业度传递给面试官。 - [2]室内场所的选择。 选择一个安静的没有干扰的地方,视频区域整洁没有多余的杂物;灯光明亮,避免人像曝光,面试官可清晰看到你;确保坐的椅子舒适,利于自己在面试过程中精神保持专注。 - [3]个人设备和网络。 确认手机电量充足,对应的相机和麦克风功能可以正常使用;关闭任何会发出提示音的设备,避免面试中收到干扰;测试设备和网络是否能正常使用,减少面试中出现断网等低级错误。疫情未止,但这不会成为找工作面试的阻碍,在疫情期间做好面试的充足准备,提高线上面试的重视度,即便现场出现突发状况,镇静并且及时与对方沟通,商量解决方案,一切都能迎刃而解。总之,只要做好十足的准备,确保一切都是最佳状态,即便从未经历过视频面试的你,也能脱颖而出。 面试某技术岗位,事先练习面试题 比如Python,小编为大家精心准备了以下面试题 1.Python是如何进行内存管理的? 答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制 一、对象的引用计数机制 Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。 引用计数增加的情况: - 1,一个对象分配一个新名称 - 2,将其放入一个容器中(如列表、元组或字典) 引用计数减少的情况: - 1,使用del语句对对象别名显示的销毁 - 2,引用超出作用域或被重新赋值 sys.getrefcount( )函数可以获得对象的当前引用计数 多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。 二、垃圾回收 - 1,当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。 - 2,当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。 三、内存池机制 Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。 - 1,Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。 - 2,Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。 - 3,对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。 2.什么是lambda函数?它有什么好处? 答:lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数 lambda函数:首要用途是指点短小的回调函数 lambda [arguments]:expression a=lambdax,y:x+y a(3,11) 3.Python里面如何实现tuple和list的转换? 答:直接使用tuple和list函数就行了,type()可以判断对象的类型 4.请写出一段Python代码实现删除一个list里面的重复元素 答: - 1,使用set函数,set(list) - 2,使用字典函数, a=[1,2,4,2,4,5,6,5,7,8,9,0] b={} b=b.fromkeys(a) c=list(b.keys()) c 5.编程用sort进行排序,然后从最后一个元素开始判断 a=[1,2,4,2,4,5,7,10,5,5,7,8,9,0,3] a.sort() last=a[-1] for i inrange(len(a)-2,-1,-1): if last==a[i]: del a[i] else:last=a[i] print(a) 6.Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别) 答:赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个。 浅拷贝:创建一个新的对象,但它包含的是对原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另外一个也会修改改变){1,完全切片方法;2,工厂函数,如list();3,copy模块的copy()函数} 深拷贝:创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变){copy模块的deep.deepcopy()函数} 7.介绍一下except的用法和作用? 答:try…except…except…[else…][finally…] 执行try下的语句,如果引发异常,则执行过程会跳到except语句。对每个except分支顺序尝试执行,如果引发的异常与except中的异常组匹配,执行相应的语句。如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层try代码中。 try下的语句正常执行,则执行else块代码。如果发生异常,就不会执行 如果存在finally语句,最后总是会执行。 8.Python中pass语句的作用是什么? 答:pass语句不会执行任何操作,一般作为占位符或者创建占位程序,whileFalse:pass 9.介绍一下Python下range()函数的用法? 答:列出一组数据,经常用在for in range()循环中 10.如何用Python来进行查询和替换一个文本字符串? 答:可以使用re模块中的sub()函数或者subn()函数来进行查询和替换, 格式:sub(replacement, string[,count=0])(replacement是被替换成的文本,string是需要被替换的文本,count是一个可选参数,指最大被替换的数量) import re p=re.compile(‘blue|white|red’) print(p.sub(‘colour’,'blue socks and red shoes’)) colour socks and colourshoes print(p.sub(‘colour’,'blue socks and red shoes’,count=1)) colour socks and redshoes subn()方法执行的效果跟sub()一样,不过它会返回一个二维数组,包括替换后的新的字符串和总共替换的数量 11.Python里面match()和search()的区别? 答:re模块中match(pattern,string[,flags]),检查string的开头是否与pattern匹配。 re模块中research(pattern,string[,flags]),在string搜索pattern的第一个匹配值。 print(re.match(‘super’, ‘superstition’).span()) (0, 5) print(re.match(‘super’, ‘insuperable’)) None print(re.search(‘super’, ‘superstition’).span()) (0, 5) print(re.search(‘super’, ‘insuperable’).span()) (2, 7) 12.用Python匹配HTML tag的时候,<.>和<.?>有什么区别? 答:术语叫贪婪匹配( <.> )和非贪婪匹配(<.?> ) 例如: test <.> : test <.?> : 13.Python里面如何生成随机数? 答:random模块 随机整数:random.randint(a,b):返回随机整数x,a<=x<=b random.randrange(start,stop,[,step]):返回一个范围在(start,stop,step)之间的随机整数,不包括结束值。 随机实数:random.random( ):返回0到1之间的浮点数 random.uniform(a,b):返回指定范围内的浮点数。 14.有没有一个工具可以帮助查找python的bug和进行静态的代码分析? 答:PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告 Pylint是另外一个工具可以进行codingstandard检查 15.如何在一个function里面设置一个全局的变量? 答:解决方法是在function的开始插入一个global声明: def f() global x 16.单引号,双引号,三引号的区别 答:单引号和双引号是等效的,如果要换行,需要符号(),三引号则可以直接换行,并且可以包含注释 如果要表示Let’s go 这个字符串 单引号:s4 = ‘Let\’s go’ 双引号:s5 = “Let’s go” s6 = ‘I realy like“python”!’ 这就是单引号和双引号都可以表示字符串的原因了 最后小编祝福大家能在2020年找到心仪的工作哈

剑曼红尘 2020-03-12 16:06:50 0 浏览量 回答数 0

回答

初识 MyBatis MyBatis 是第一个支持自定义 SQL、存储过程和高级映射的类持久框架。MyBatis 消除了大部分 JDBC 的样板代码、手动设置参数以及检索结果。MyBatis 能够支持简单的 XML 和注解配置规则。使 Map 接口和 POJO 类映射到数据库字段和记录。 MyBatis 的特点 那么 MyBatis 具有什么特点呢?或许我们可以从如下几个方面来描述 MyBatis 中的 SQL 语句和主要业务代码分离,我们一般会把 MyBatis 中的 SQL 语句统一放在 XML 配置文件中,便于统一维护。 解除 SQL 与程序代码的耦合,通过提供 DAO 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。SQL 和代码的分离,提高了可维护性。 MyBatis 比较简单和轻量 本身就很小且简单。没有任何第三方依赖,只要通过配置 jar 包,或者如果你使用 Maven 项目的话只需要配置 Maven 以来就可以。易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。 屏蔽样板代码 MyBatis 回屏蔽原始的 JDBC 样板代码,让你把更多的精力专注于 SQL 的书写和属性-字段映射上。 编写原生 SQL,支持多表关联 MyBatis 最主要的特点就是你可以手动编写 SQL 语句,能够支持多表关联查询。 提供映射标签,支持对象与数据库的 ORM 字段关系映射 ORM 是什么?对象关系映射(Object Relational Mapping,简称ORM) ,是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 提供 XML 标签,支持编写动态 SQL。 你可以使用 MyBatis XML 标签,起到 SQL 模版的效果,减少繁杂的 SQL 语句,便于维护。 MyBatis 整体架构 MyBatis 最上面是接口层,接口层就是开发人员在 Mapper 或者是 Dao 接口中的接口定义,是查询、新增、更新还是删除操作;中间层是数据处理层,主要是配置 Mapper -> XML 层级之间的参数映射,SQL 解析,SQL 执行,结果映射的过程。上述两种流程都由基础支持层来提供功能支撑,基础支持层包括连接管理,事务管理,配置加载,缓存处理等。 接口层 在不与Spring 集成的情况下,使用 MyBatis 执行数据库的操作主要如下: InputStream is = Resources.getResourceAsStream("myBatis-config.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(is); sqlSession = factory.openSession(); 其中的SqlSessionFactory,SqlSession是 MyBatis 接口的核心类,尤其是 SqlSession,这个接口是MyBatis 中最重要的接口,这个接口能够让你执行命令,获取映射,管理事务。 数据处理层 配置解析 在 Mybatis 初始化过程中,会加载 mybatis-config.xml 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configration 对象中。之后,根据该对象创建SqlSessionFactory 对象。待 Mybatis 初始化完成后,可以通过 SqlSessionFactory 创建 SqlSession 对象并开始数据库操作。 SQL 解析与 scripting 模块 Mybatis 实现的动态 SQL 语句,几乎可以编写出所有满足需要的 SQL。 Mybatis 中 scripting 模块会根据用户传入的参数,解析映射文件中定义的动态 SQL 节点,形成数据库能执行的SQL 语句。 SQL 执行 SQL 语句的执行涉及多个组件,包括 MyBatis 的四大核心,它们是: Executor、StatementHandler、ParameterHandler、ResultSetHandler。SQL 的执行过程可以用下面这幅图来表示 MyBatis 层级结构各个组件的介绍(这里只是简单介绍,具体介绍在后面): SqlSession: ,它是 MyBatis 核心 API,主要用来执行命令,获取映射,管理事务。接收开发人员提供 Statement Id 和参数。并返回操作结果。Executor :执行器,是 MyBatis 调度的核心,负责 SQL 语句的生成以及查询缓存的维护。StatementHandler : 封装了JDBC Statement 操作,负责对 JDBC Statement 的操作,如设置参数、将Statement 结果集转换成 List 集合。ParameterHandler : 负责对用户传递的参数转换成 JDBC Statement 所需要的参数。ResultSetHandler : 负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。TypeHandler : 用于 Java 类型和 JDBC 类型之间的转换。MappedStatement : 动态 SQL 的封装SqlSource : 表示从 XML 文件或注释读取的映射语句的内容,它创建将从用户接收的输入参数传递给数据库的 SQL。Configuration: MyBatis 所有的配置信息都维持在 Configuration 对象之中。 基础支持层 反射模块 Mybatis 中的反射模块,对 Java 反射进行了很好的封装,提供了简易的 API,方便上层调用,并且对反射操作进行了一系列的优化,比如,缓存了类的 元数据(MetaClass)和对象的元数据(MetaObject),提高了反射操作的性能。 类型转换模块 Mybatis 的别名机制,能够简化配置文件,该机制是类型转换模块的主要功能之一。类型转换模块的另一个功能是实现 JDBC 类型与 Java 类型的转换。在 SQL 语句绑定参数时,会将数据由 Java 类型转换成 JDBC 类型;在映射结果集时,会将数据由 JDBC 类型转换成 Java 类型。 日志模块 在 Java 中,有很多优秀的日志框架,如 Log4j、Log4j2、slf4j 等。Mybatis 除了提供了详细的日志输出信息,还能够集成多种日志框架,其日志模块的主要功能就是集成第三方日志框架。 资源加载模块 该模块主要封装了类加载器,确定了类加载器的使用顺序,并提供了加载类文件和其它资源文件的功能。 解析器模块 该模块有两个主要功能:一个是封装了 XPath,为 Mybatis 初始化时解析 mybatis-config.xml配置文件以及映射配置文件提供支持;另一个为处理动态 SQL 语句中的占位符提供支持。 数据源模块 Mybatis 自身提供了相应的数据源实现,也提供了与第三方数据源集成的接口。数据源是开发中的常用组件之一,很多开源的数据源都提供了丰富的功能,如连接池、检测连接状态等,选择性能优秀的数据源组件,对于提供ORM 框架以及整个应用的性能都是非常重要的。 事务管理模块 一般地,Mybatis 与 Spring 框架集成,由 Spring 框架管理事务。但 Mybatis 自身对数据库事务进行了抽象,提供了相应的事务接口和简单实现。 缓存模块 Mybatis 中有一级缓存和二级缓存,这两级缓存都依赖于缓存模块中的实现。但是需要注意,这两级缓存与Mybatis 以及整个应用是运行在同一个 JVM 中的,共享同一块内存,如果这两级缓存中的数据量较大,则可能影响系统中其它功能,所以需要缓存大量数据时,优先考虑使用 Redis、Memcache 等缓存产品。 Binding 模块 在调用 SqlSession 相应方法执行数据库操作时,需要制定映射文件中定义的 SQL 节点,如果 SQL 中出现了拼写错误,那就只能在运行时才能发现。为了能尽早发现这种错误,Mybatis 通过 Binding 模块将用户自定义的Mapper 接口与映射文件关联起来,系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作,从而避免上述问题。注意,在开发中,我们只是创建了 Mapper 接口,而并没有编写实现类,这是因为 Mybatis 自动为 Mapper 接口创建了动态代理对象。 MyBatis 核心组件 在认识了 MyBatis 并了解其基础架构之后,下面我们来看一下 MyBatis 的核心组件,就是这些组件实现了从 SQL 语句到映射到 JDBC 再到数据库字段之间的转换,执行 SQL 语句并输出结果集。首先来认识 MyBatis 的第一个核心组件 SqlSessionFactory 对于任何框架而言,在使用该框架之前都要经历过一系列的初始化流程,MyBatis 也不例外。MyBatis 的初始化流程如下 String resource = "org/mybatis/example/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); sqlSessionFactory.openSession(); 上述流程中比较重要的一个对象就是SqlSessionFactory,SqlSessionFactory 是 MyBatis 框架中的一个接口,它主要负责的是 MyBatis 框架初始化操作 为开发人员提供SqlSession 对象 SqlSessionFactory 有两个实现类,一个是 SqlSessionManager 类,一个是 DefaultSqlSessionFactory 类 DefaultSqlSessionFactory : SqlSessionFactory 的默认实现类,是真正生产会话的工厂类,这个类的实例的生命周期是全局的,它只会在首次调用时生成一个实例(单例模式),就一直存在直到服务器关闭。 SqlSessionManager : 已被废弃,原因大概是: SqlSessionManager 中需要维护一个自己的线程池,而使用MyBatis 更多的是要与 Spring 进行集成,并不会单独使用,所以维护自己的 ThreadLocal 并没有什么意义,所以 SqlSessionManager 已经不再使用。 ####SqlSessionFactory 的执行流程 下面来对 SqlSessionFactory 的执行流程来做一个分析 首先第一步是 SqlSessionFactory 的创建 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 1 从这行代码入手,首先创建了一个 SqlSessionFactoryBuilder 工厂,这是一个建造者模式的设计思想,由 builder 建造者来创建 SqlSessionFactory 工厂 然后调用 SqlSessionFactoryBuilder 中的 build 方法传递一个InputStream 输入流,Inputstream 输入流中就是你传过来的配置文件 mybatis-config.xml,SqlSessionFactoryBuilder 根据传入的 InputStream 输入流和environment、properties属性创建一个XMLConfigBuilder对象。SqlSessionFactoryBuilder 对象调用XMLConfigBuilder 的parse()方法,流程如下。 XMLConfigBuilder 会解析/configuration标签,configuration 是 MyBatis 中最重要的一个标签,下面流程会介绍 Configuration 标签。 MyBatis 默认使用 XPath 来解析标签,关于 XPath 的使用,参见 https://www.w3school.com.cn/xpath/index.asp 在 parseConfiguration 方法中,会对各个在 /configuration 中的标签进行解析 重要配置 说一下这些标签都是什么意思吧 properties,外部属性,这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。 <properties> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="root" /> </properties> 一般用来给 environment 标签中的 dataSource 赋值 <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </dataSource> </environment> 还可以通过外部属性进行配置,但是我们这篇文章以原理为主,不会介绍太多应用层面的操作。 settings ,MyBatis 中极其重要的配置,它们会改变 MyBatis 的运行时行为。 settings 中配置有很多,具体可以参考 https://mybatis.org/mybatis-3/zh/configuration.html#settings 详细了解。这里介绍几个平常使用过程中比较重要的配置 一般使用如下配置 <settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> </settings> typeAliases,类型别名,类型别名是为 Java 类型设置的一个名字。 它只和 XML 配置有关。 <typeAliases> <typeAlias alias="Blog" type="domain.blog.Blog"/> </typeAliases> 当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。 typeHandlers,类型处理器,无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。 在 org.apache.ibatis.type 包下有很多已经实现好的 TypeHandler,可以参考如下 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很方便的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。 objectFactory,对象工厂,MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。 public class ExampleObjectFactory extends DefaultObjectFactory { public Object create(Class type) { return super.create(type); } public Object create(Class type, List constructorArgTypes, List constructorArgs) { return super.create(type, constructorArgTypes, constructorArgs); } public void setProperties(Properties properties) { super.setProperties(properties); } public boolean isCollection(Class type) { return Collection.class.isAssignableFrom(type); } } 然后需要在 XML 中配置此对象工厂 <objectFactory type="org.mybatis.example.ExampleObjectFactory"> <property name="someProperty" value="100"/> </objectFactory> plugins,插件开发,插件开发是 MyBatis 设计人员给开发人员留给自行开发的接口,MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。MyBatis 允许使用插件来拦截的方法调用包括:Executor、ParameterHandler、ResultSetHandler、StatementHandler 接口,这几个接口也是 MyBatis 中非常重要的接口,我们下面会详细介绍这几个接口。 environments,MyBatis 环境配置,MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中 使用相同的 SQL 映射。 这里注意一点,虽然 environments 可以指定多个环境,但是 SqlSessionFactory 只能有一个,为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties); databaseIdProvider ,数据库厂商标示,MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 <databaseIdProvider type="DB_VENDOR"> <property name="SQL Server" value="sqlserver"/> <property name="DB2" value="db2"/> <property name="Oracle" value="oracle" /> </databaseIdProvider> mappers,映射器,这是告诉 MyBatis 去哪里找到这些 SQL 语句,mappers 映射配置有四种方式 上面的一个个属性都对应着一个解析方法,都是使用 XPath 把标签进行解析,解析完成后返回一个 DefaultSqlSessionFactory 对象,它是 SqlSessionFactory 的默认实现类。这就是 SqlSessionFactoryBuilder 的初始化流程,通过流程我们可以看到,初始化流程就是对一个个 /configuration 标签下子标签的解析过程。 SqlSession 在 MyBatis 初始化流程结束,也就是 SqlSessionFactoryBuilder -> SqlSessionFactory 的获取流程后,我们就可以通过 SqlSessionFactory 对象得到 SqlSession 然后执行 SQL 语句了。具体来看一下这个过程‘ 在 SqlSessionFactory.openSession 过程中我们可以看到,会调用到 DefaultSqlSessionFactory 中的 openSessionFromDataSource 方法,这个方法主要创建了两个与我们分析执行流程重要的对象,一个是 Executor 执行器对象,一个是 SqlSession 对象。执行器我们下面会说,现在来说一下 SqlSession 对象 SqlSession 对象是 MyBatis 中最重要的一个对象,这个接口能够让你执行命令,获取映射,管理事务。SqlSession 中定义了一系列模版方法,让你能够执行简单的 CRUD 操作,也可以通过 getMapper 获取 Mapper 层,执行自定义 SQL 语句,因为 SqlSession 在执行 SQL 语句之前是需要先开启一个会话,涉及到事务操作,所以还会有 commit、 rollback、close 等方法。这也是模版设计模式的一种应用。 MapperProxy MapperProxy 是 Mapper 映射 SQL 语句的关键对象,我们写的 Dao 层或者 Mapper 层都是通过 MapperProxy 来和对应的 SQL 语句进行绑定的。下面我们就来解释一下绑定过程 这就是 MyBatis 的核心绑定流程,我们可以看到 SqlSession 首先调用 getMapper 方法,我们刚才说到 SqlSession 是大哥级别的人物,只定义标准(有一句话是怎么说的来着,一流的企业做标准,二流的企业做品牌,三流的企业做产品)。 SqlSession 不愿意做的事情交给 Configuration 这个手下去做,但是 Configuration 也是有小弟的,它不愿意做的事情直接甩给小弟去做,这个小弟是谁呢?它就是 MapperRegistry,马上就到核心部分了。MapperRegistry 相当于项目经理,项目经理只从大面上把握项目进度,不需要知道手下的小弟是如何工作的,把任务完成了就好。最终真正干活的还是 MapperProxyFactory。看到这段代码 Proxy.newProxyInstance ,你是不是有一种恍然大悟的感觉,如果你没有的话,建议查阅一下动态代理的文章,这里推荐一篇 (https://www.jianshu.com/p/95970b089360) 也就是说,MyBatis 中 Mapper 和 SQL 语句的绑定正是通过动态代理来完成的。 通过动态代理,我们就可以方便的在 Dao 层或者 Mapper 层定义接口,实现自定义的增删改查操作了。那么具体的执行过程是怎么样呢?上面只是绑定过程,别着急,下面就来探讨一下 SQL 语句的执行过程。 MapperProxyFactory 会生成代理对象,这个对象就是 MapperProxy,最终会调用到 mapperMethod.execute 方法,execute 方法比较长,其实逻辑比较简单,就是判断是 插入、更新、删除 还是 查询 语句,其中如果是查询的话,还会判断返回值的类型,我们可以点进去看一下都是怎么设计的。 很多代码其实可以忽略,只看我标出来的重点就好了,我们可以看到,不管你前面经过多少道关卡处理,最终都逃不过 SqlSession 这个老大制定的标准。 我们以 selectList 为例,来看一下下面的执行过程。 这是 DefaultSqlSession 中 selectList 的代码,我们可以看到出现了 executor,这是什么呢?我们下面来解释。 Executor 还记得我们之前的流程中提到了 Executor(执行器) 这个概念吗?我们来回顾一下它第一次出现的位置。 由 Configuration 对象创建了一个 Executor 对象,这个 Executor 是干嘛的呢?下面我们就来认识一下 Executor 的继承结构 每一个 SqlSession 都会拥有一个 Executor 对象,这个对象负责增删改查的具体操作,我们可以简单的将它理解为 JDBC 中 Statement 的封装版。 也可以理解为 SQL 的执行引擎,要干活总得有一个发起人吧,可以把 Executor 理解为发起人的角色。 首先先从 Executor 的继承体系来认识一下 如上图所示,位于继承体系最顶层的是 Executor 执行器,它有两个实现类,分别是BaseExecutor和 CachingExecutor。 BaseExecutor 是一个抽象类,这种通过抽象的实现接口的方式是适配器设计模式之接口适配 的体现,是Executor 的默认实现,实现了大部分 Executor 接口定义的功能,降低了接口实现的难度。BaseExecutor 的子类有三个,分别是 SimpleExecutor、ReuseExecutor 和 BatchExecutor。 SimpleExecutor : 简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象) ReuseExecutor : 可重用执行器,这里的重用指的是重复使用 Statement,它会在内部使用一个 Map 把创建的Statement 都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement 对象,如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement 对象,并将其缓存起来。因为每一个 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的 Statement作用域是同一个 SqlSession。 BatchExecutor : 批处理执行器,用于将多个 SQL 一次性输出到数据库 CachingExecutor: 缓存执行器,先从缓存中查询结果,如果存在就返回之前的结果;如果不存在,再委托给Executor delegate 去数据库中取,delegate 可以是上面任何一个执行器。 Executor 的创建和选择 我们上面提到 Executor 是由 Configuration 创建的,Configuration 会根据执行器的类型创建,如下 这一步就是执行器的创建过程,根据传入的 ExecutorType 类型来判断是哪种执行器,如果不指定 ExecutorType ,默认创建的是简单执行器。它的赋值可以通过两个地方进行赋值: 可以通过 标签来设置当前工程中所有的 SqlSession 对象使用默认的 Executor <settings> <!--取值范围 SIMPLE, REUSE, BATCH --> <setting name="defaultExecutorType" value="SIMPLE"/> </settings> 另外一种直接通过Java对方法赋值的方式 session = factory.openSession(ExecutorType.BATCH); Executor 的具体执行过程 Executor 中的大部分方法的调用链其实是差不多的,下面是深入源码分析执行过程,如果你没有时间或者暂时不想深入研究的话,给你下面的执行流程图作为参考。 我们紧跟着上面的 selectList 继续分析,它会调用到 executor.query 方法。 当有一个查询请求访问的时候,首先会经过 Executor 的实现类 CachingExecutor ,先从缓存中查询 SQL 是否是第一次执行,如果是第一次执行的话,那么就直接执行 SQL 语句,并创建缓存,如果第二次访问相同的 SQL 语句的话,那么就会直接从缓存中提取。 上面这段代码是从 selectList -> 从缓存中 query 的具体过程。可能你看到这里有些觉得类都是什么东西,我想鼓励你一下,把握重点,不用每段代码都看,从找到 SQL 的调用链路,其他代码想看的时候在看,看源码就是很容易发蒙,容易烦躁,但是切记一点,把握重点。 上面代码会判断缓存中是否有这条 SQL 语句的执行结果,如果没有的话,就再重新创建 Executor 执行器执行 SQL 语句,注意, list = doQuery 是真正执行 SQL 语句的过程,这个过程中会创建我们上面提到的三种执行器,这里我们使用的是简单执行器。 到这里,执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler 继续执行。下面我们来认识一下 StatementHandler 上面代码会判断缓存中是否有这条 SQL 语句的执行结果,如果没有的话,就再重新创建 Executor 执行器执行 SQL 语句,注意, list = doQuery 是真正执行 SQL 语句的过程,这个过程中会创建我们上面提到的三种执行器,这里我们使用的是简单执行器。 到这里,执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler 继续执行。下面我们来认识一下 StatementHandler StatementHandler 的继承结构 有没有感觉和 Executor 的继承体系很相似呢?最顶级接口是四大组件对象,分别有两个实现类 BaseStatementHandler 和 RoutingStatementHandler,BaseStatementHandler 有三个实现类, 他们分别是 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler。 RoutingStatementHandler : RoutingStatementHandler 并没有对 Statement 对象进行使用,只是根据StatementType 来创建一个代理,代理的就是对应Handler的三种实现类。在MyBatis工作时,使用的StatementHandler 接口对象实际上就是 RoutingStatementHandler 对象。 BaseStatementHandler : 是 StatementHandler 接口的另一个实现类,它本身是一个抽象类,用于简化StatementHandler 接口实现的难度,属于适配器设计模式体现,它主要有三个实现类 SimpleStatementHandler: 管理 Statement 对象并向数据库中推送不需要预编译的SQL语句。PreparedStatementHandler: 管理 Statement 对象并向数据中推送需要预编译的SQL语句。CallableStatementHandler:管理 Statement 对象并调用数据库中的存储过程。 StatementHandler 的创建和源码分析 我们继续来分析上面 query 的调用链路,StatementHandler 的创建过程如下 MyBatis 会根据 SQL 语句的类型进行对应 StatementHandler 的创建。我们以预处理 StatementHandler 为例来讲解一下 执行器不仅掌管着 StatementHandler 的创建,还掌管着创建 Statement 对象,设置参数等,在创建完 PreparedStatement 之后,我们需要对参数进行处理了。 如 如果用一副图来表示一下这个执行流程的话我想是这样 这里我们先暂停一下,来认识一下第三个核心组件 ParameterHandler ParameterHandler - ParameterHandler 介绍 ParameterHandler 相比于其他的组件就简单很多了,ParameterHandler 译为参数处理器,负责为 PreparedStatement 的 sql 语句参数动态赋值,这个接口很简单只有两个方法 ParameterHandler 只有一个实现类 DefaultParameterHandler , 它实现了这两个方法。 getParameterObject: 用于读取参数setParameters: 用于对 PreparedStatement 的参数赋值ParameterHandler 的解析过程 上面我们讨论过了 ParameterHandler 的创建过程,下面我们继续上面 parameterSize 流程 这就是具体参数的解析过程了,下面我们来描述一下 下面用一个流程图表示一下 ParameterHandler 的解析过程,以简单执行器为例 我们在完成 ParameterHandler 对 SQL 参数的预处理后,回到 SimpleExecutor 中的 doQuery 方法 上面又引出来了一个重要的组件那就是 ResultSetHandler,下面我们来认识一下这个组件 ResultSetHandler - ResultSetHandler 简介 ResultSetHandler 也是一个非常简单的接口 ResultSetHandler 是一个接口,它只有一个默认的实现类,像是 ParameterHandler 一样,它的默认实现类是DefaultResultSetHandler ResultSetHandler 解析过程 MyBatis 只有一个默认的实现类就是 DefaultResultSetHandler,DefaultResultSetHandler 主要负责处理两件事 处理 Statement 执行后产生的结果集,生成结果列表 处理存储过程执行后的输出参数 按照 Mapper 文件中配置的 ResultType 或 ResultMap 来封装成对应的对象,最后将封装的对象返回即可。 其中涉及的主要对象有: ResultSetWrapper : 结果集的包装器,主要针对结果集进行的一层包装,它的主要属性有 ResultSet : Java JDBC ResultSet 接口表示数据库查询的结果。 有关查询的文本显示了如何将查询结果作为java.sql.ResultSet 返回。 然后迭代此ResultSet以检查结果。 TypeHandlerRegistry: 类型注册器,TypeHandlerRegistry 在初始化的时候会把所有的 Java类型和类型转换器进行注册。 ColumnNames: 字段的名称,也就是查询操作需要返回的字段名称 ClassNames: 字段的类型名称,也就是 ColumnNames 每个字段名称的类型 JdbcTypes: JDBC 的类型,也就是 java.sql.Types 类型 ResultMap: 负责处理更复杂的映射关系 在 DefaultResultSetHandler 中处理完结果映射,并把上述结构返回给调用的客户端,从而执行完成一条完整的SQL语句。 内容转载自:CSDN博主:cxuann 原文链接:https://blog.csdn.net/qq_36894974/article/details/104132876?depth_1-utm_source=distribute.pc_feed.none-task&request_id=&utm_source=distribute.pc_feed.none-task

问问小秘 2020-03-05 15:44:27 0 浏览量 回答数 0

问题

【精品问答】python技术1000问(1)

问问小秘 2019-12-01 21:57:48 454222 浏览量 回答数 19

回答

如何掌握牢靠Go语言的容器? 容器相对来说更偏重细节一些,如果想掌握的更牢靠的话呢,还是要多看一下代码,重点给大家几个提示 Go语言的并发初步有哪两个特别重要的特点? **GO语言的协程并发操作或者说协程的资源池,其调度策略有两个: ** 1、没有优先级,没有API能设置优先级,正是因为它一切都是靠Go语言自身的一个调度器来听调度,才能保证它的高效率,这点非常重要。 2、调度的策略是可抢占的,假如说一个任务它长时间的占用CPU,那么它是有可能被购入天的这个调度器给其抢占过来,让其其的任务来做运行,这是两个最重要的特点。 GO语言调度的单元goroutine的应用场景是什么? 使用JAVA或者C编写网络程序时,一个线程来处理一个http请求, 但是对于资源的利用率不高。而Go语言实现了轻量级线程的机制,GO语言在底层封装了所有的系统调用,自己实现了一个调度器,这种设计在操作系统的代码中非常多见。比如现代的操作系统基本都会封装一个软件的Timer,同时可以提供上万个软Timer同时工作,而这只是基于数量很少的硬件timer实现的,而GO语言中的并发也是如此,他是基于线程的调度池,这种调度的单元在Go语言中被称为goroutine。 GO语言与其它并发模型最大的区别是什么? 宏观GO语言与其它并发模型最大的不同,就是其推荐使用通信的这种方式来替代共享内存。当资源需要在goroutine之间进行共享的时候,实际上就是这个资源,或者说这个信息通过通道在goroutine之间进行通信的过程。因为这个锁,一般来说都是用在这个共享内存当中的,因为如果说大家阅读GO语言的相关代码,就可以看到这个channel,它实际上是基于锁来保证并发安全。 然而,这也不代表GO语言当中只能使用channel来进行一些操作,其也具备锁这方面的知识。因为现实当中,这个锁还是有一定它现实的意义和现实的要求,因为这个锁它最关键的一个意义就是它能保证资源能在并发的操作当中有一个合理的调度情况和调度策略。其中跟这个最重要,或者说最关联性最强的一个概念就是原子操作。 GO语言中的原子操作具体实现过程是怎样的? 对于原子操作,在其逻辑下,按照它书面的定义上来讲,是指不会被调度器打断的操作。对原子操作实际上就是不存在中间状态的一种操作,要不就全成功,要不全失败,这个在我们在用并发方式来调动某任务,或者说来设计某种并发系统的情况下,这种名字操作我发现是非常重要的设计理念之一。 并发与并行具体概念及实际区分是怎样的? 有一个比较重要的一个概念,就是并发与并行,其实并发与并行,它实际上具体的含义是不一样的,并发实际上是把任务在不同的时间点交给同样一个处理器来进行处理,在同一个时间点,任务不会同时进行,只是任务感觉自己正在执行,因为其那会儿可能正在堵塞状态或者说是就绪状态,其不知道自己被暂停了,以为已经被调度走了,可能自己没有感知,但是实际上CPU所有权已经不在这个任务身上了。 并行比并发更高级一些,它实际上是把每个任务都交给独立的处理器去进行完成,但同一时间点,任务在一定程度上实际上是同时在执行的。一般来说,并发的性能是要比并行更重要一些,在1.5版本之前,我们需要人工去设置GO调度器最多能运行在多少个CPU上,但是在最新的GO版本当中,已经不需要这个相关的操作。 详细介绍一下并发程序中的竞争态? 并发系统设计最初始的这一个概念就是并发程序设计当中一个竞合的概念,或者也叫竞争态。假如说我要记录一个文件的阅读量,但是这个文件或者说这个网页,可能它的阅读渠道有非常多,有可能通过引擎通过微信通过APP等等这些渠道,这些渠道的话呢,它的阅读也都是并发的,这就会涉及到同样一个变量,被多个协程的所共同访问的情况。具体代码如下: 对于GO语言并发体系中的主推的通信机制是什么? channel是GO语言并发体系中的主推的通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个 channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。 GO语言当中,它实际上是大家协同的机制,通过这种方式让几个goroutine之间做达到一个协调的效果,那么每个goroutine当中,实际上channel都是一个特殊的类型,它实际上是可以发送数据。比如现在想发送一个int类型的数据,那么channel就要定义一个发送int数据的一个管道。 那么GO语言当中,提倡使用通讯的方式来代替共享内存的方式来做goroutine,或者说并发之间的一个协同。channel如果我们后续阅读它的代码就会知道,它是保证协程安全,并且它遵循这个先入先出的原则来让这个储蓄方读取获得数据,而且它能保证顺序,正是这两个特性,可以让这个channel替代共享内存,因为它的如果顺序有所改变的话,它实际上也是有会有问题。 详细介绍GO语言中关于通道的声明涉及哪些方面? 1.经典方式声明 通过使用chan类型,其声明方式如下: var name chan type 其中type表示通道内的数据类型;name:通道的变量名称,不过这样创建的通道只是空值 nil,一般来说都是通道都是通过make函数创建的。 2.make方式 make函数可以创建通道格式如下: name := make(chan type) 3.创建带有缓冲的通道 后面会讲到缓冲通道的概念,这里先说他的定义方式 name := make(chan type, size) 其中type表示通道内的数据类型;name:通道的变量名称,size代表缓冲的长度。 具体介绍通道数据收发的详细过程有哪些? 通道的数据发送 通道当中发送数据的操作服务是这样的这样的一个大于号加上一个减号。 chan <- value 注意,如果是发送给一个没有缓冲的一个通道。假如说数据没有被接收的话,那么这个发送操作将持续被注册,也就是说就是channel这个语句就直接被注册到这,假如说没有任何的协程去读到他或者其他语句去读到这个产品,那么这个语句就被注册掉了。但GO语言是能发现的,如果其一直在堵塞的话,那实际上就造成死锁,GO语言的编译器实际上能发现的有点错误。 假如说,首先创建一个int型的通道,然后直接尝试发送一个数据给它,编译会报错,然后呢,数据的这个数据的接收的话,实际上就是把这个点号的位置跟那个大于号的位置做了一个调换。其实把这个双方的位置做了一个调换之后,是实际上就是都做了一个允许的操作。这其中的话呢,还有一种比较特殊的一个读取操作是其可以忽略到接收到的数据,因为不管管道中发出的数据,如果没读的话就堵塞到这,那么如果你觉得这个语句你也不需要,那么你可以把那个变量给它忽略掉。 2.通道的数据接收 通道接收数据的操作符也是<-,具体有以下几种方式 - 1) 阻塞接收数据 阻塞模式接收数据时,将接收变量作为<-操作符的左值,格式如下: data := <-ch 执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。 如需要忽略接收的数据,则将data变量省略,具体格式如下: <-ch - 2) 非阻塞接收数据 使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下: data, ok := <-ch 非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。一般只配合select语句配合定时器做超时检测时使用。 关于通道数据收发有哪些需要注意的事项? 通道数据在进行输入收发的时候,必须要在两个不同的goroutine当中进行,因在同一个goroutine当中,收发的这些语句实际上都是堵塞的,你可能在同一个goroutine当中,它的这个函数已经在那边阻塞住了,或者说程序已经在那边阻塞住了,它已经停在那了,你后面有一句你能执行不到,所以说通道的收发必须在两个不同的goroutine之间来进行,在同一个goroutine之间的这个收发操作的话,实际上是没有意义的。 接收将持续堵塞,直到发送方发送出去,如果接收方接收,然后通道中没有发送方数据时,接收方也会发送,直到发送方到发送数据为止。就是刚才说的这个一体两面,这个发送方假如说没有人读的话,发送方会堵塞,假如说没有人写的话,那么接收方也会发生堵塞,这两边实际上都会有一个堵塞的情况。那么这个通道的收发的话呢,一般来说一次只能收一一个元素,假如说这个是一个有缓冲的一个通道,我通过一次不操作的话,实际上也只不过读出一个元素。不能把它一些缓冲区所有元素都读出来。 聊一下生产者消费者模式具体内容有哪些? 介绍一下生产者消费者模式,从GO语言的这个并发模型来看,也就是说假如说咱们站在一个比较高的一个高度来看,其实利用channel的确能达到共享内存的目的。这个channel的性质与在读写状态且保证顺序的共享内存并无不同。甚至我们可以说这个是基于消息队列的封装程度可以比共享内存来的更安全,所以说呢,这个在这个GO语言当中,或者说在GO语言的这个设计风格当中的话呢,其这个生产者消费者模式实现起来会相对来说比较简单一些。我们先介绍一下什么是生产者消费者。 就这个这这张图当中的话呢,就是一个典型的那种消费的问题, 就是说我是生产者的话我会生产一些产品,然后放到这个仓库当中,消费者的话会从那个仓库当中去取商品,这个可以说是消息队列,还有包括卡夫卡那些比较经典的相应队列当中,都会用到的这么一个设计模式,或者说其们从本质上来说的话,都是基于这样一个设计模式,交易的生产者是谁?消费者是谁?这个消息队列的话是。这个生产者消费者模式的话呢,实际上也成为有缓冲有限缓冲问题,它是一个并发的一个经典的案例,因为我们知道这个商品仓库的库房大小是有限的,也就是说生产者不能无限的去生产商品,一旦这个库房爆掉的话,它是它是必须要中止自己的生产,消费者也是不能无限地获取消息。 假如仓库是空的话,那这个消费者的这个相关的情况也需要被阻塞。那么怎么在这个生产者跟消费者之间保证商品不丢失。这就是生产者与消费者之间最核心的内容。先来看一下这个Java当中生产者消费者的这种实现到底是什么样的。这个可以说是一个最经典的这么样一个实现。这个Java当中是没有channel,那么它只能通过什么呢,只能通过信号量和一个一个log,也就是说一个忽视服务态度,这两个这两个配合信号量和所配合才能共同完成,这样一个生产者消费者这么一个相关的工作。 GO语言并发实战详细过程梳理 在现在这个远程办公的这一个大的背景下,积累了大量重复的文件,因为很可能大家都不断的在不同的群里发相同的文件,发相同的这个报表,以及一些相同的视频等等这些需要学习的材料,那么怎么把这些文件都找出来,然后把这些相同文件都给删掉了,这实际上是并发课的一个实践的一个内容,因为这个创业型的这个方案的话,它的代码相对来说比较长。 如何使用GO语言清理PC机中的文件,详细代码及注释如下: package main import ( // "fmt" // fmt 包使用函数实现 I/O 格式化(类似于 C 的 printf 和 scanf 的函数), 格式化参数源自C,但更简单 "io/ioutil" //"sync" //"time" ) func PrintRepreatFile(path string, fileNameSizeMap map[string]int64, exFileList []string) { fs, _ := ioutil.ReadDir(path) for _, file := range fs { if file.IsDir() { PrintRepreatFile(path+"/"+file.Name(), fileNameSizeMap, exFileList)//遍历整个文件系统,如果是目录则递归调用 } else { if file.Size() > 1000000 {//设定文件清理阈值,如果大于一定大小再进行清理 fileSize := fileNameSizeMap[file.Name()]//通过查哈希表的方式来确定,有无重名且大小相同的文件。 if fileSize == file.Size() { fmt.Println(path + "/" + file.Name())//如果有则打印出来 exFileList = append(exFileList, path+file.Name())//将结果记入切片当中 } else { fileNameSizeMap[file.Name()] = file.Size() } } } } } func main() { //方式一 fileNameSizeMap := make(map[string]int64, 10000) exFileList := make([]string, 100, 1000) PrintRepreatFile("E:/test", fileNameSizeMap, exFileList) } 这个程序在GO语言的环境下可以直接运行使用,其中有几个知识点,也是咱们前文提到过的,首先是切片的大小一定要设定的相对合适一些,如果容量不够大造成频繁扩容非常浪费资源。二是哈希表也就是map没有并发安全的属于,在我们这个未引入并发的程序中可以使用,如果有并发操作,那么map不再适用了。 可能很多人被GO语言的在并发性能所吸引入坑的,GO语言之父也就是UNIX之父Ken Thompson明显给出了很多建议,根据笔者在操作系统方面的相关经验来看,GO语言设计中经常参考UNIX内核的设计思路。比如硬定时器的数量有限,无法满足系统实际运行需要,所以在内核代码中就会看到基于硬件定时器的软件定时器的方案,而软件定时器的数量可以比硬件定时器多几百倍。 这样的理念明显融合到了 goroutine之中,由于其它编程语言往往直接通过系统级别的线程来实现并发功能,但是这样的方式往往会是大马拉小车,造成系统资源的浪费。因此GO语言封装了所有的系统操作,实现了更加轻量级的协程-goroutine。只要使用关键字(go)就可以启动协程,对比C++、JAVA的多线程并发模型,GO的协程更简单明了。 当然协程之间的消息通信与并发控制也是非常重要的一环。在GO语言借鉴了Message Queue的消息队列机制替代共享内存的方式进行协程间通信,其中管道channel作为基本的数据类型,保证并发时的操作安全。而且管道的引入还带来很多实践中非常实用的功能,比如可以方便实现生产者、消费者等并发设计模式,而这些设计模式在其它使用共享存内存的并发模型中实现起相关功能来非常的繁锁。 在GO语言中在调用函数前加入go 关键字,就能启动一个协程,也就是一个并发,但是我们上面的程序如果把调用方式改为: go PrintRepreatFile("E:/test", fileNameSizeMap, exFileList) 你会发现程序会直接退出,什么都没做,所以GO语言的并发对于初学者来说还是有一定门槛的,比如上例中如果想设计成一个并行的程序,如何让多个协程共同来帮忙找出重复的文件其实还是要费一番周折的。

剑曼红尘 2020-04-13 11:06:46 0 浏览量 回答数 0

问题

【Java学习全家桶】1460道Java热门问题,阿里百位技术专家答疑解惑

管理贝贝 2019-12-01 20:07:15 27612 浏览量 回答数 19

问题

在 berserkJS 中无缝使用 Wind.js:报错

kun坤 2020-06-07 14:00:40 0 浏览量 回答数 1

问题

程序员报错QA大分享(1)

问问小秘 2020-06-18 15:46:14 8 浏览量 回答数 1

问题

【精品问答】Python二级考试题库

珍宝珠 2019-12-01 22:03:38 1146 浏览量 回答数 2

问题

我们一起来游戏 (四)——(AS3零基础做AIR卡牌网游):报错

kun坤 2020-06-08 11:10:24 2 浏览量 回答数 1

回答

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

问题

使用IBPP在C++中操作FireBird/Interbase数据库:报错

kun坤 2020-06-06 13:49:18 0 浏览量 回答数 1

问题

Nginx性能为什么如此吊

小柒2012 2019-12-01 21:20:47 15038 浏览量 回答数 3
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站