首页 文章 精选 留言 我的

精选列表

搜索[面试],共4912篇文章
优秀的个人博客,低调大师

【Java面试】几种常见的分布式锁

前言 随着互联网的发展,各种高并发、海量处理的场景越来越多。为了实现高可用、可扩展的系统,常常使用分布式,这样避免了单点故障和普通计算机cpu、内存等瓶颈。 但是分布式系统也带来了数据一致性的问题,比如用户抢购秒杀商品多台机器共同执行出现超卖等。有些同学容易将分布式锁与线程安全混淆,线程安全是指的线程间的协同。如果是多个进程间的协同需要用到分布式锁,本文总结了几种常见的分布式锁。 基于数据库 悲观锁—事务 比如用户抢购秒杀商品的场景,多台机器都接收到了抢购的请求,可以将获取库存、判断有货、用户付款、扣减库存等多个数据库操作放到一个事务,这样当一台机器与数据库建立链接请求了抢购商品这个事务,另外的机器只能等这个机器将请求完成才能操作数据库。在实际应用场景中,常常库存与交易是两个独立的系统,这时的事务是一个分布式事务,需要用到两段式、三段式提交。 优点:是比较安全的一种实现方法。 缺点:在高并发的场景下开销是不能容忍的。容易出现数据库死锁等情况。 乐观锁—基于版本号 乐观锁常常用于分布式系统对数据库某张特定表执行update操作。考虑线上选座的场景,用户A和B同时选择了某场次电影的一个座位,都去将座位的状态设置为已售。 设想这样的执行序列: 1、用户A判断该座位为未售状态; 2、用户B判断该座位为未售状态; 3、用户A执行update座位为已售; 4、用户B执行update座位为已售。 这样会出现同一个座位售出两次的情况,解决方案是在这张数据库表中增加一个版本号的字段。执行操作前读取当前数据库表中的版本号,在执行update语句时将版本号放在where语句中,如果更新了记录则说明成功,如果没有更新记录,则说明此次update失败。 加了乐观锁的执行序列: 1、用户A查询该座位,得到该座位是未售状态,版本号是5; 2、用户B查询该座位,得到该座位是未售状态,版本号是5; 3、用户A执行update语句将座位状态更新为已售,版本号更新为6; 4、用户B执行update语句时此时这个座位的记录版本号为6,没有版本号为5的这个座位的记录,执行失败。 优点:乐观锁的性能高于悲观锁,并不容易出现死锁。 缺点:乐观锁只能对一张表的数据进行加锁,如果是需要对多张表的数据操作加分布式锁,基于版本号的乐观锁是办不到的。 基于memcached memcached可以基于add命令加锁。memcached的add指令是指如果有这个key,add命令则失败,如果没有这个key,则add命令成功。并且memcached支持设置过期时间的add原子操作。并发add同一个key也只有一个会成功。 基于memcached的add指令加分布式锁的思路为:定义一个key为分布式锁的key,如果add一个带过期时间的key成功则执行相应的业务操作,执行完判断锁是否过期,如果锁过期则不删除锁,如果锁没过期则删除锁。带过期时间是防止出现机器宕机,一直不能释放锁。 很多人基于memcached实现的分布式锁没有判断锁是否过期,执行完相应的业务操作直接删除锁会出现以下问题。 设想这样的执行序列: 1、机器A成功add一个带过期时间的key; 2、机器A在执行业务操作时出现较长时间的停顿,比如出现了较长时间的GC pause; 3、机器A还未在较长的停顿中恢复出来,锁已经过期,机器B成功add一个带过期时间的锁; 4、此时机器A从较长的停顿中恢复出来,执行完相应业务操作,删除了机器Badd的锁; 5、此时机器B的业务操作是在没有锁保护的情况下执行的。 但是memcached并没有提供一个判断key是否存在的操作,需要依赖于加锁的时候的时钟与执行完业务操作的时钟相减获得执行时间,将执行时间与锁的过期时间进行对比。或者将锁key对应的value设置为当前时间加上过期时间的时钟,执行完相应的业务操作获取锁key的值与当前时钟进行对比。 注:过期时间一定要长于业务操作的执行时间。 优点:性能高于基于数据库的实现方式。 基于redis redis提供了setNx原子操作。基于redis的分布式锁也是基于这个操作实现的,setNx是指如果有这个key就set失败,如果没有这个key则set成功,但是setNx不能设置超时时间。 基于redis组成的分布式锁解决方案为: 1、setNx一个锁key,相应的value为当前时间加上过期时间的时钟; 2、如果setNx成功,或者当前时钟大于此时key对应的时钟则加锁成功,否则加锁失败退出; 3、加锁成功执行相应的业务操作(处理共享数据源); 4、释放锁时判断当前时钟是否小于锁key的value,如果当前时钟小于锁key对应的value则执行删除锁key的操作。 注:这对于单点的redis能很好地实现分布式锁,如果redis集群,会出现master宕机的情况。如果master宕机,此时锁key还没有同步到slave节点上,会出现机器B从新的master上获取到了一个重复的锁。 设想以下执行序列: 1、机器AsetNx了一个锁key,value为当前时间加上过期时间,master更新了锁key的值; 2、此时master宕机,选举出新的master,新的master正同步数据; 3、新的master不含锁key,机器BsetNx了一个锁key,value为当前时间加上过期时间; 这样机器A和机器B都获得了一个相同的锁;解决这个问题的办法可以在第3步进行优化,内存中存储了锁key的value,在执行访问共享数据源前再判断内存存储的锁key的value与此时redis中锁key的value是否相等如果相等则说明获得了锁,如果不相等则说明在之前有其他的机器修改了锁key,加锁失败。同时在第4步不仅仅判断当前时钟是否小于锁key的value,也可以进一步判断存储的value值与此时的value值是否相等,如果相等再进行删除。 此时的执行序列: 1、机器AsetNx了一个锁key,value为当前时间加上过期时间,master更新了锁key的值; 2、此时,master宕机,选举出新的master,新的master正同步数据; 3、机器BsetNx了一个锁key,value为此时的时间加上过期时间; 4、当机器A再次判断内存存储的锁与此时的锁key的值不一样时,机器A加锁失败; 5、当机器B再次判断内存存储的锁与此时的锁key的值一样,机器B加锁成功。 注:如果是为了效率而使用分布式锁,例如:部署多台定时作业的机器,在同一时间只希望一台机器执行一个定时作业,在这种场景下是允许偶尔的失败的,可以使用单点的redis分布式锁;如果是为了正确性而使用分布式锁,最好使用再次检查的redis分布式锁,再次检查的redis分布式锁虽然性能下降了,但是正确率更高。 基于zookeeper 基于zookeeper的分布式锁大致思路为: 1、客户端尝试创建ephemeral类型的znode节点/lock; 2、如果客户端创建成功则加锁成功,可以执行访问共享数据源的操作,如果客户端创建失败,则证明有别的客户端加锁成功,此次加锁失败; 3、如果加锁成功当客户端执行完访问共享数据源的操作,则删除znode节点/lock。 基于zookeeper实现分布式锁不需要设置过期时间,因为ephemeral类型的节点,当客户端与zookeeper创建的session在一定时间(session的过期时间内)没有收到心跳,则认为session过期,会删除客户端创建的所有ephemeral节点。 但是这样会出现两个机器共同持有锁的情况。设想以下执行序列。 1、机器A创建了znode节点/lock; 2、机器A执行相应操作,进入了较长时间的GC pause; 3、机器A与zookeeper的session过期,相应的/lock节点被删除; 4、机器B创建了znode节点/lock; 5、机器A从较长的停顿中恢复; 6、此时机器A与机器B都认为自己获得了锁。 与基于redis的分布式锁,基于zookeeper的锁可以增加watch机制,当机器创建节点/lock失败的时候可以进入等待,当/lock节点被删除的时候zookeeper利用watch机制通知机器。但是这种增加watch机制的方式只能针对较小客户端集群,如果较多客户端集群都在等待/lock节点被删除,当/lock节点被删除时,zookeeper要通知较多机器,对zookeeper造成较大的性能影响。这就是所谓的羊群效应。 优化的大致思路为: 1、客户端调用创建名为“lock/number_lock_”类型为EPHEMERAL_SEQUENTIAL的节点; 2、客户端获取lock节点下所有的子节点; 3、判断自己是否是序号最小的节点的,如果是最小的节点则加锁成功,如果不是序号最小的节点,则在比自己小的并且最接近的节点注册监听; 4、当被关注的节点删除后,再次获取lock节点下的所有子节点,判断是否是最小序号,如果是最小序号则加锁成功; 优化后的思路,虽然能一定程度避免羊群效应,但是也不能避免两个机器共同持有锁的情况。 工作一到五年的程序员朋友面对目前的技术无从下手,感到很迷茫可以加群744677563,里面有阿里Java高级大牛直播讲解知识点,分享知识,课程内容都是各位老师多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!

优秀的个人博客,低调大师

Python面试真实笔试题总结(附加实现答案)

1、一行代码实现1—100之和 2、如何在一个函数内部修改全局变量 3、列出5个Python标准库 os :提供了不少与操作系统相关联的函数 sys :通常用于命令行参数 re :正则表达式匹配 math :数学运算 datetime :处理日期时间 4、字典如何删除键和合并两个字典 del和update方法 5、Python实现列表去重的方法 先通过集合去重再转为列表 6、Python基本内建数据类型有哪些 整型 ——int、 布尔型 ——bool、 字符串 ——str 列表 ——list、 元组 ——tuple、 字典 ——dict 7、Python2和Python3的range(100)的区别 Python2中的range返回的是一个列表, Python3中的range返回的是一个迭代值,节约内存 8、一句话解释什么样的语言能够用装饰器 函数可以作为参数传递的语言,可以使用装饰器 9、简述with方法打开处理文件帮我们做了什么 打开文件在进行读写操作时可能会出现一些异常状况,如果按照常规的file.open写法,我们需要try,except,finally,做异常判断,并且文件最终不管遇到什么情况,都要执行finally的file.close()关闭文件 with方法帮我们实现了finally中file.close()操作 10、列表[1,2,3,4,5],请使用map()函数输出[1,4,9,16,25],并使用列表推导式提取出大于10的数,最终输出[16,25] 11、Python中生成随机整数、随机小数、0—1之间小数的方法 12、避免转义给字符串加哪个字母表示原始字符串 r,表示需要原始字符串,不转义特殊字符 13、Python中断言方法举例 assert()方法,断言成功,则程序继续执行;断言失败,则程序报错 14、<div class="nam">中国</div>,用正则匹配出标签里面的内容(“中国”),其中class的类名是不确定的 15、Python2和Python3的区别,列出5个 (1) Python3使用print必须要以小括号包裹打印内容,比如:print(“hello”);Python2既可以使用带小括号的方式,也可以使用一个空格来分隔打印内容,比如:print “hello"。 (2) range(1,10) 在Python2中返回的是列表,在Python3中返回的是迭代器。 (3) Python2中使用的是ASCII编码;Python3中默认使用utf-8编码。 (4) Python2中unicode表示字符串序列,str表示字节序列;Python3中str表示字符串序列,byte表示字节序列。 (5) Python3中用input,Python2中用row_input,都输入为str。 (6) Python3中/表示真除,%表示取余,//结果取整;Python2中带上小数点/表示真除,%表示取余,//结果取整。 16、列出Python中可变数据类型和不可变数据类型,并简述原理 不可变数据类型: 数值型、字符串型string和元组tuple 不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象(一个地址),用id()方法可以打印对象的id (id方法的返回值就是对象的内存地址) 可变数据类型 :列表list和字典dict; 允许变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。 17、s = "ajldjlajfdljfddd",去重并从小到大排序输出"adfjl"。 注: join()函数 语法: 'sep'.join(seq) 参数说明 sep:分隔符。可以为空 seq:要连接的元素序列、字符串、元组、字典 上面的语法即:以sep作为分隔符,将seq所有的元素合并成一个新的字符串 返回值: 返回一个以分隔符sep连接各个元素后生成的字符串 18、用lambda函数实现两个数相乘 19、字典根据键从小到大排序 dict={"name":"zs","age":18,"city":"深圳","tel":"1362626627"} 20、a=(1,)b=(1),c=("1") 分别是什么类型的数据? 21、正则re.complie作用 re.compile是将正则表达式编译成一个对象,加快速度,并重复使用 22、列表推导式求列表所有奇数并构造新列表,a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 23、filter方法求出列表所有奇数并构造新列表,a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 24、利用collections库的Counter方法统计字符串每个单词出现的次数"kjalfj;ldsjafl;hdsllfdhg;lahfbl;hl;ahlf;h" 25、字符串a = "not 404 found 张三 99 深圳",每个词中间是空格,用正则过滤掉英文和数字,最终输出"张三 深圳" 26、两个列表[1,5,7,9]和[2,2,6,8]合并为[1,2,2,5,6,7,8,9] 27、x="abc",y="def",z=["d","e","f"],分别求出x.join(y)和x.join(z)返回的结果 join()括号里面的是可迭代对象,x插入可迭代对象中间,形成字符串 28、[[1,2],[3,4],[5,6]]一行代码展开该列表,得出[1,2,3,4,5,6] 29、正则表达式匹配中,(.*)和(.*?)匹配区别? (.*) 是 贪婪匹配 ,会把满足正则的尽可能多的往后匹配 (.*?) 是 非贪婪匹配 ,会把满足正则的尽可能少匹配 30、请列出你会的任意一种统计图(条形图、折线图等)绘制的开源库,第三方也行 pychart、matplotlib 31、log日志中,我们需要用时间戳记录error,warning等的发生时间,请用datetime模块打印当前时间戳 “2018-04-05 10:11:27” 32、写一段自定义异常代码 自定义异常用raise抛出异常 33、[1,2,3]+[4,5,6]的结果是多少? 34、a="hello"和b="你好"编码成bytes类型 35、a="张明 98分",用re.sub,将98替换为100 36、举例说明zip()函数用法 zip()函数用于将 一个或多个可迭代的对象作为参数 ,将对象中对应的元素 配对 打包成一个个 元组 ,然后 返回由这些元组组成的列表 。 zip()参数可以接受任何类型的序列,同时也可以有两个以上的参数;当传入参数的长度不同时, zip能自动以最短序列长度为准进行截取,获得元组 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。 37、python中交换两个数值 38、举例说明异常模块中try except else finally的相关意义 try..except..else没有捕获到异常,执行else语句 try..except..finally不管是否捕获到异常,都执行finally语句 39、提高python运行效率的方法 (1)使用生成器,因为可以节约大量内存 (2)循环代码优化,避免过多重复代码的执行 (3)核心模块用Cython PyPy等,提高效率 (4)多进程、多线程、协程 (5)多个if elif条件判断,可以把最有可能先发生的条件放到前面写,这样可以减少程序判断的次数,提高效率 40、遇到bug如何处理 (1)细节上的错误,通过print()打印,能执行到print()说明一般上面的代码没有问题,分段检测程序是否有问题,如果是js的话可以alert或console.log (2)如果涉及一些第三方框架,会去查官方文档或者一些技术博客。 (3)对于bug的管理与归类总结,一般测试将测试出的bug用teambin等bug管理工具进行记录,然后我们会一条一条进行修改,修改的过程也是理解业务逻辑和提高自己编程逻辑缜密性的方法,我也都会收藏做一些笔记记录。 (4)导包问题、城市定位多音字造成的显示错误问题 41、使用pop和del删除字典中的"name"字段,dic={"name":"zs","age":18} 42、列出常见的状态码和意义 200 OK 请求正常处理完毕 204 No Content 请求成功处理,没有实体的主体返回 206 Partial Content GET范围请求已成功处理 301 Moved Permanently 永久重定向,资源已永久分配新URI 302 Found 临时重定向,资源已临时分配新URI 303 See Other 临时重定向,期望使用GET定向获取 304 Not Modified 发送的附带条件请求未满足 307 Temporary Redirect 临时重定向,POST不会变成GET 400 Bad Request 请求报文语法错误或参数错误 401 Unauthorized 需要通过HTTP认证,或认证失败 403 Forbidden 请求资源被拒绝 404 Not Found 无法找到请求资源(服务器无理由拒绝) 500 Internal Server Error 服务器故障或Web应用故障 503 Service Unavailable 服务器超负载或停机维护 43、求三个方法打印结果 fn("one",1)直接将键值对传给字典; fn("two",2)因为字典在内存中是可变数据类型,所以指向同一个地址,传了新的参数后,会相当于给字典增加键值对 fn("three",3,{})因为传了一个新字典,所以不再是原先默认参数的字典 44、list=[2,3,5,4,9,6],从小到大排序,不许用sort,输出[2,3,4,5,6,9] list=[2,3,5,4,9,6],从小到大排序,不许用sort,输出[2,3,4,5,6,9] 45、正则匹配,匹配日期2018-03-20 url='https://sycm.taobao.com/bda/tradinganaly/overview/get_summary.json?dateRange=2018-03-20%7C2018-03-20&dateType=recent1&device=1&token=ff25b109b&_=1521595613462' 46、a="%.03f"%1.3335,计算a的结果,保留两位小数 round()方法返回浮点数x的四舍五入值 47、int("1.4"),int(1.4)输出结果? int("1.4")报错,int(1.4)输出1 答案是自己一个一个敲出来的,如有差错请留言,欢迎指正!

优秀的个人博客,低调大师

java面试- Java并发编程(十)——线程池(1)

线程池的作用 减少资源的开销减少了每次创建线程、销毁线程的开销。 提高响应速度每次请求到来时,由于线程的创建已经完成,故可以直接执行任务,因此提高了响应速度。 提高线程的可管理性线程是一种稀缺资源,若不加以限制,不仅会占用大量资源,而且会影响系统的稳定性。因此,线程池可以对线程的创建与停止、线程数量等等因素加以控制,使得线程在一种可控的范围内运行,不仅能保证系统稳定运行,而且方便性能调优。 线程池的实现原理 线程池一般由两种角色构成:多个工作线程 和 一个阻塞队列。 工作线程工作线程是一组已经处在运行中的线程,它们不断地向阻塞队列中领取任务执行。 阻塞队列阻塞队列用于存储工作线程来不及处理的任务。当工作线程都在执行任务时,到来的新任务就只能暂时在阻塞队列中存储。 ThreadPoolExecutor的使用 创建线程池 通过如下代码即可创建一个线程池: new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue, handler); 1 corePoolSize:基本线程数量它表示你希望线程池达到的一个值。线程池会尽量把实际线程数量保持在这个值上下。 maximumPoolSize:最大线程数量这是线程数量的上界。如果实际线程数量达到这个值: 阻塞队列未满:任务存入阻塞队列等待执行 阻塞队列已满:调用饱和策略 keepAliveTime:空闲线程的存活时间当实际线程数量超过corePoolSize时,若线程空闲的时间超过该值,就会被停止。PS:当任务很多,且任务执行时间很短的情况下,可以将该值调大,提高线程利用率。 timeUnit:keepAliveTime的单位 runnableTaskQueue:任务队列这是一个存放任务的阻塞队列,可以有如下几种选择: ArrayBlockingQueue它是一个由数组实现的阻塞队列,FIFO。 LinkedBlockingQueue它是一个由链表实现的阻塞队列,FIFO。吞吐量通常要高于ArrayBlockingQueue。fixedThreadPool使用的阻塞队列就是它。它是一个无界队列。 SynchronousQueue它是一个没有存储空间的阻塞队列,任务提交给它之后必须要交给一条工作线程处理;如果当前没有空闲的工作线程,则立即创建一条新的工作线程。cachedThreadPool用的阻塞队列就是它。它是一个无界队列。 PriorityBlockingQueue它是一个优先权阻塞队列。 handler:饱和策略当实际线程数达到maximumPoolSize,并且阻塞队列已满时,就会调用饱和策略。JDK1.5由四种饱和策略: AbortPolicy默认。直接抛异常。 CallerRunsPolicy只用调用者所在的线程执行任务。 DiscardOldestPolicy丢弃任务队列中最久的任务。 DiscardPolicy丢弃当前任务。 提交任务 可以向ThreadPoolExecutor提交两种任务:Callable和Runnable。 Callable该类任务有返回结果,可以抛出异常。通过submit函数提交,返回Future对象。可通过get获取执行结果。 Runnable该类任务只执行,无法获取返回结果,并在执行过程中无法抛异常。通过execute提交。 关闭线程池 关闭线程池有两种方式:shutdown和shutdownNow,关闭时,会遍历所有的线程,调用它们的interrupt函数中断线程。但这两种方式对于正在执行的线程处理方式不同。 shutdown()仅停止阻塞队列中等待的线程,那些正在执行的线程就会让他们执行结束。 shutdownNow()不仅会停止阻塞队列中的线程,而且会停止正在执行的线程。 ThreadPoolExecutor运行机制 当有请求到来时: 若当前实际线程数量少于corePoolSize,即使有空闲线程,也会创建一个新的工作线程; 若当前实际线程数量处于corePoolSize和maximumPoolSize之间,并且阻塞队列没满,则任务将被放入阻塞队列中等待执行; 若当前实际线程数量小于maximumPoolSize,但阻塞队列已满,则直接创建新线程处理任务; 若当前实际线程数量已经达到maximumPoolSize,并且阻塞队列已满,则使用饱和策略。 设置合理的线程池大小 任务一般可分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线程池。 CPU密集型任务尽量使用较小的线程池,一般为CPU核心数+1。因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。 IO密集型任务可以使用稍大的线程池,一般为2*CPU核心数。IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。 混合型任务可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。因为如果划分之后两个任务执行时间相差甚远,那么先执行完的任务就要等后执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,得不偿失。

优秀的个人博客,低调大师

java面试-Java并发编(十一)——线程池(2)

Executor两级调度模型 在HotSpot虚拟机中,Java中的线程将会被一一映射为操作系统的线程。在Java虚拟机层面,用户将多个任务提交给Executor框架,Executor负责分配线程执行它们;在操作系统层面,操作系统再将这些线程分配给处理器执行。 Executor结构 Executor框架中的所有类可以分成三类: 任务任务有两种类型:Runnable和Callable。 任务执行器Executor框架最核心的接口是Executor,它表示任务的执行器。Executor的子接口为ExecutorService。ExecutorService有两大实现类:ThreadPoolExecutor和ScheduledThreadPoolExecutor。 执行结果Future接口表示异步的执行结果,它的实现类为FutureTask。 线程池 Executors工厂类可以创建四种类型的线程池,通过Executors.newXXX即可创建。 1. FixedThreadPool public static ExecutorService newFixedThreadPool(int nThreads){ return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()); } 1 2 3 它是一种固定大小的线程池; corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads; keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效; 阻塞队列采用了LinkedBlockingQueue,它是一个无界队列; 由于阻塞队列是一个无界队列,因此永远不可能拒绝任务; 由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。 2. CachedThreadPool public static ExecutorService newCachedThreadPool(){ return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>()); } 1 2 3 它是一个可以无限扩大的线程池; 它比较适合处理执行时间比较小的任务; corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大; keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死; 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。 3. SingleThreadExecutor public static ExecutorService newSingleThreadExecutor(){ return new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()); } 1 2 3 它只会创建一条工作线程处理任务; 采用的阻塞队列为LinkedBlockingQueue; 4. ScheduledThreadPool 它用来处理延时任务或定时任务。 它接收SchduledFutureTask类型的任务,有两种提交任务的方式: scheduledAtFixedRate scheduledWithFixedDelay SchduledFutureTask接收的参数: time:任务开始的时间 sequenceNumber:任务的序号 period:任务执行的时间间隔 它采用DelayQueue存储等待的任务 DelayQueue内部封装了一个PriorityQueue,它会根据time的先后时间排序,若time相同则根据sequenceNumber排序; DelayQueue也是一个无界队列; 工作线程的执行过程: 工作线程会从DelayQueue取已经到期的任务去执行; 执行结束后重新设置任务的到期时间,再次放回DelayQueue

优秀的个人博客,低调大师

NET工程师求职面试必杀技

一、.NET框架 开发人员应该熟悉.NET FrameWork体系结构和基本原理,熟悉CLR(公共语言运行时)和MSIL(中间语言),熟悉.NET框架中的委托、线程、序列化、集合、垃圾回收机制、反射等内容。 二、面向对象软件开发 开发人员应该熟悉面向对象软件开发(OOP)基本概念,熟悉面向对象软件开发中的类、继承、封装、多态等概念,具备良好的面向对象软件开发思想和设计原则。 不论是采用何种软件开发框架,还是使用不同的开发语言,面向对象软件开发在其中畅通无阻。 三、反射 通过反射技术可以得知类的详细信息,包括成员变量、属性、方法,可以实现动态加载技术(Java也有类似技术)。 四、正则表达式 用正则表达式可以轻松实现对数据的校验和修改、替换等,这个最先在Perl语言中的技术,现在已经各个编程语言广泛吸收了,在.Net中掌握了这门技术对文本操作也是轻松平常。 五、C# 开发人员应该熟练掌握C#这门面向对象编程语言,虽然.NET框架支持多种编程语言,但C#无疑是最简洁、使用者最广泛和功能最强大的一种。 C#是一门年轻的语言,它的出现,让更多的开发者爱上了C#,脱离了VB和ASP的阵营,造成离别。 六、ASP.NET 开发人员应该理解ASP.NET的页面生命周期、熟悉配置文件的格式、熟悉ASP.NET的各种服务器控件和数据控件、了解ASP.NET中的各种对象,了解ASP.NET2.0新特性。 七、数据库 开发人员需了解各种主流数据库,熟悉数据库的规范设计、精通SQL及存储过程、触发器的编写。 八、AJAX技术(JAVASCRIPT和XML) XML和JAVASCRIPT的跨平台特性,在实际软件开发中的运用越来越广泛,由于现代软件对用户界面和WEB前端的日益重视,集JAVASCRIPT和XML技术于大成的AJAX正在流行,特别是在基于WEB2.0的网站开发中。 JAVASCRIPT和XML技术已经出现多年,此前一直不痛不痒,但由于集JAVASCRIPT和XML于大成的AJAX技术的出现及其在互联网上的不断应用,又重新焕发出生命活力,将众多的开发者牢牢系住,在2006年红透了整个IT界,2007年AJAX技术将继续红火。 九、设计模式 有了IDE,懂一些基本语法就可以编写.Net程序了,再熟练一些就可以编写出高效率的程序了,如果想程序更灵活,将来更容易扩展和适应更多的需求变化,你就需要了解、掌握设计模式了。 十、软件体系架构(拳头) 优秀的软件离不开优秀的软件体系架构,作为开发人员,要想在IT行业更进一步,需要具备系统的、良好的软件体系架构思维,从而从更高层次决定软件的整体系统框架。 结束语:无论多可怕的武器,也比不上人类的信心。所以人类最厉害的武器。便是自己的信心。相信你自己,做最好的自己,你就会成功! 本文转自周金桥51CTO博客,原文链接:http://blog.51cto.com/zhoufoxcn/166980 ,如需转载请自行联系原作者

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。