• 关于

    异常处理程序出问题什么情况

    的搜索结果

回答

这几个问题回答起来没什么意思,我主要分析分析面试官为什么问你这几个问题。问题 1 主要应该是想了解你对调用其他程序或接口时出现的异常情况是如何处理的,毕竟程序的运行并不总是一帆风顺,如果由于其他的接口出现了问题,而你没有处理好这些问题继而引发你写的程序出错或崩溃,那确实不是一个优秀的程序员所做的。程序员不仅应该了解用户按常理出牌,按既定路线完成程序的情况,还应该做好发生各种异常情况的处理。问题 2 主要应该是想了解你是怎样评估你写的程序的,是通过怎样的手段了解的程序的运行情况,是如何优化你的程序的。实现功能并不是程序员的唯一目标,在实现功能的情况下做一个优秀的、高效的程序也是一项艰巨的工程。问题 3 主要应该是想了解你对程序体系结构,或者说是架构的理解,程序不是简单的代码堆积,是有思想线路支撑,有模块协作分工的。作为程序员,虽然可能不会去实际写某一块的东西,但是至少应该了解这些模块的功能以及所有模块之间的联系和他们是如何协作工作的。就好比实现一个接口,你需要处理输入,进行逻辑判断,数据库操作,遇到错误的响应,返回结果等等,这里每一块都是相当细致的。
a123456678 2019-12-02 03:03:23 0 浏览量 回答数 0

问题

Windows Phone 7 - 如何退出程序:报错

在WPF程序中,你可以调用“App.Exit()”,你的程序就终止了。但是在 WP7 上面就没那么简单了,因为没有 Exit() 方法。 如果你抛出一个未被处理的异常,你的程序将被...
kun坤 2020-06-20 11:52:16 1 浏览量 回答数 1

问题

10个Java异常的常见问题:报错

这里总结了10个关于JAVA学习过程中同学们经常会问到的JAVA异 常问题,希望对同学们的学习有所帮助。 1、检查型异常vs非检查型异常 简单的说,检查型异常是指需要在方法中自己捕获异常处理或者声明抛出异...
kun坤 2020-06-09 11:37:57 3 浏览量 回答数 1

问题

ECS Windows端口异常占用的处理方法是什么

问题现象 服务器应用使用的端口被异常占用导致服务启动失败,例如:Windows上安装的Tomcat无法启动,TCP 80端口已经被其它应用占用。 问题原因 应用程序需要使用的TCP/UD...
boxti 2019-12-01 22:10:16 1477 浏览量 回答数 1

回答

回 1楼无衣蒹葭的帖子 是这样的,我是一个服务器要一直获取用户传来的资源进行处理再回送 但是如果我在中途进行aos_http_io_deinitialize(); 程序就相当于返回到更高一级的线程,在那个线程里通过批处理去掉cmd如果异常 批处理会终止 不进行aos_http_io_deinitialize();的话 会在当前子线程模块运行 能确保程序的正确运行 ------------------------- 回 1楼无衣蒹葭的帖子 我没有采取外调 exe方式 而是直接把sdk代码加到我原有的代码里了 ------------------------- 回 1楼无衣蒹葭的帖子 只调用aos_pool_destroy(p);不调用aos_http_io_deinitialize();会有什么问题? 或者有没有其他的办法 不让程序回到更高一级? ------------------------- 回 5楼无衣蒹葭的帖子 嗯,确实是多线程,那样的话如果在主函数循环里进行清理会影响到其他线程的处理对吧? ------------------------- 回 5楼无衣蒹葭的帖子 是不是只要运行一次aos_http_io_deinitialize last就可以全部清理 我在主循环里设置一个空闲的期间 然后进行 aos_http_io_initialize("oss_test", 0); aos_http_io_deinitialize(); 两句 这样行嘛? ------------------------- 回 5楼无衣蒹葭的帖子 我试了一下,发现只要我加了aos_http_io_deinitialize(); 程序就错了= =!!! 后面调系统命令只要异常就不会继续执行了 不知道这是什么情况。。。 ------------------------- 回 9楼无衣蒹葭的帖子 这个问题蛮蛋疼的。。只要我用aos_http_io_initialize cmd就会回到主线程状态 出异常就直接错了 哪怕只是删一个不存在的文件夹。。都不会继续执行批处理了 ------------------------- 回 9楼无衣蒹葭的帖子 只调aos_pool_destroy(p) 不调aos_http_io_initialize 然后 定期重启整个程序 会不会有什么危害? 从目前的状态来看 我确实不能调aos_http_io_initialize。。。除非我把整个osssdk单独做一个进程去调用 ------------------------- ReCSDKOSS不清理运行环境会怎样? 确实每次上传下载内存都有少量的增长
九日王朝 2019-12-02 01:28:29 0 浏览量 回答数 0

回答

1.第一个问题通过数据版本,也就是所谓的乐观锁解决。 2.先写日志log,然后ack机制。其实很多这种方式被很多应用所用到比如mysql。 3.用户注册本身这个功能不属于高频调用,所以性能上不需要考虑太多,直接悲观锁实现即可。而且这种可能性非常低,就算失败,那么返回给用户一个能理解的失败信息即可。######回复 @sixliu:谢谢回复靠谱的回答真不多我再等等看...######回复 @花歌:第二个可能没太理解你的场景######谢谢咯~1和3可以,我看看还有什么别的方案,差不多也就这么做了,2的话再考虑考虑吧感觉还是有点不适用场景###### 三个问题,其实就是同一个并发的问题,###### 都是并发中会出现的问题。 1说的在内存里的情况,就是2。 1说的在数据库中的情况,就是3。 在数据库中,数据库自己会有锁来解决这个问题,遇到这种情况会修改失败,程序中捕获这种异常做处理返回给前台就可以了。 在内存中,单机单进程单线程,会有顺序,因此没有问题。多机或多进程或多线程操作同一数据,会出现此问题。一种实现方式是加锁,相当于仿照数据库那样的实现,内存正在被修改时,其他的修改会被阻塞或者异常终止。另一种方式是通过队列实现顺序操作,所有的修改都发送到一个程序修改。######让我想想,嗯差不多,回答比较靠谱,谢了先其实...我用的是nodejs全异步操作,前面的数据库操作没完成,后面的也可以进入函数,如果网络延迟,就会造成执行完成顺序和开始执行顺序不一致...等等想一会再问你哈###### 1.是设计上的问题 两个操作如果有先后顺序 就得先后执行  一个操作完了之后再下一个操作 不可能明知道有一前一后却还要非得一起 2.这个就是非常典型的数据库事务 就是保证多个不相关的操作的原子性 只要其中一个出问题就全部回滚 不存在有的成功有的失败 事务还是个挺复杂的东西 mongodb都还不支持事务 多服务器之间分布式的事务也是有些麻烦的  3.同时的操作数据库自己会进行锁的处理 对数据库来说还是一前一后  如果某个字段设置了唯一索引 那后面的那个必然会出错 代码里正常处理就可以了 所以用户名不唯一的处理有两个地方 一个是在插入之前 一个是在插入时抛出唯一索引异常   当然也可以在新建用户这一整个操作上加锁 全局同时只能有一个用户在新建 不过这样可能效率不高 ######问题1现实情况就是这样用户以为他的操作有顺序但基于连接池算是并发操作即时不用池那也是异步操作不能保证顺序所以只能考虑数据库锁时间戳问题2还没到数据库呢...只考虑多个内存中的对象操作问题3现在就是这样处理的###### 1.加锁 2.加事务控制 3.异常捕获与处理 工作不满一年吧######不好意思...工作6年多了开发经验10多年问题1暂时用乐观锁解决了问题2事务控制个毛线问题你可能是没读清内存中的几个对象而已和数据库无关就是事务也得自己实现这话谁都会说我想听的是备忘录模式这种...到底怎么做能优雅点还是我从需求设计上可能有问题问题3靠数据库唯一约束出错返回太暴力现在就是这么做的也可以数据库加锁怕影响性能###### 1,updateusersetstatus=2wherestatus=3andid=1; 2,用户名设置唯一索引。###### 可以用现在拷贝上操作,再合并的方法解决。1、按顺序合并。2、按状态合并。3、按索引合并。
优选2 2020-06-09 10:36:32 0 浏览量 回答数 0

回答

1.第一个问题通过数据版本,也就是所谓的乐观锁解决。 2.先写日志log,然后ack机制。其实很多这种方式被很多应用所用到比如mysql。 3.用户注册本身这个功能不属于高频调用,所以性能上不需要考虑太多,直接悲观锁实现即可。而且这种可能性非常低,就算失败,那么返回给用户一个能理解的失败信息即可。######回复 @sixliu : 谢谢回复 靠谱的回答真不多 我再等等看...######回复 @花歌 : 第二个 可能没太理解你的场景######谢谢咯~ 1和3可以,我看看还有什么别的方案,差不多也就这么做了,2的话 再考虑考虑吧 感觉还是有点不适用场景###### 三个问题,其实就是同一个并发的问题,###### 都是并发中会出现的问题。 1说的在内存里的情况,就是2。 1说的在数据库中的情况,就是3。 在数据库中,数据库自己会有锁来解决这个问题,遇到这种情况会修改失败,程序中捕获这种异常做处理返回给前台就可以了。 在内存中,单机单进程单线程,会有顺序,因此没有问题。多机或多进程或多线程操作同一数据,会出现此问题。一种实现方式是加锁,相当于仿照数据库那样的实现,内存正在被修改时,其他的修改会被阻塞或者异常终止。另一种方式是通过队列实现顺序操作,所有的修改都发送到一个程序修改。######让我想想,嗯 差不多,回答比较靠谱,谢了先 其实...我用的是nodejs 全异步操作,前面的数据库操作没完成,后面的也可以进入函数,如果网络延迟,就会造成执行完成顺序和开始执行顺序不一致... 等等想一会再问你哈###### 1. 是设计上的问题  两个操作如果有先后顺序  就得先后执行   一个操作完了之后再下一个操作   不可能明知道有一前一后 却还要非得一起 2. 这个就是非常典型的数据库事务   就是保证多个不相关的操作的原子性  只要其中一个出问题就全部回滚  不存在有的成功有的失败  事务还是个挺复杂的东西   mongodb都还不支持事务  多服务器之间分布式的事务也是有些麻烦的   3. 同时的操作 数据库自己会进行锁的处理  对数据库来说还是一前一后    如果某个字段设置了唯一索引  那后面的那个必然会出错  代码里正常处理就可以了   所以用户名不唯一的处理有两个地方  一个是在插入之前  一个是在插入时抛出唯一索引异常      当然也可以在新建用户这一整个操作上加锁   全局同时只能有一个用户在新建  不过这样可能效率不高  ######问题1 现实情况就是这样 用户以为他的操作有顺序 但基于连接池 算是并发操作 即时不用池 那也是异步操作 不能保证顺序 所以只能考虑数据库锁 时间戳 问题2 还没到数据库呢... 只考虑多个内存中的对象操作 问题3 现在就是这样处理的###### 1.加锁 2.加事务控制 3.异常捕获与处理 工作不满一年吧######不好意思... 工作6年多了 开发经验10多年 问题1 暂时用乐观锁解决了 问题2 事务控制个毛线 问题你可能是没读清 内存中的几个对象而已 和数据库无关 就是事务也得自己实现 这话谁都会说 我想听的是 备忘录模式 这种... 到底怎么做能优雅点 还是我从需求设计上可能有问题 问题3 靠数据库唯一约束出错返回太暴力 现在就是这么做的 也可以数据库加锁 怕影响性能###### 1,update user set status=2 where status=3 and id=1; 2,用户名设置唯一索引。###### 可以用现在拷贝上操作,再合并的方法解决。1、按顺序合并。2、按状态合并。3、按索引合并。
爱吃鱼的程序员 2020-05-29 20:15:24 0 浏览量 回答数 0

问题

ECS Windows调整进程CPU占用示例是什么

适用场景:     部分ECS服务器由于特殊需求,需要对系统内运行的进程进行CPU限制,来保障业务的正常运行,避免CPU争抢。如4核CPU,前3核用来跑数据库和Web服务...
boxti 2019-12-01 22:06:58 1311 浏览量 回答数 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

问题

【推荐】Windows系统异常重启以及蓝屏的处理方法是什么

Windows 系统下,蓝屏(BSOD, Blue Sceen of Death)是客户有时会遇到的错误,Windows 操作系统在遇到异常的情况下,为了防止数据丢失,系统自动崩溃蓝屏...
boxti 2019-12-01 22:06:15 1737 浏览量 回答数 0

回答

1.内存泄漏:基础 对于初学者来说,将内存泄漏视为一种疾病,将Java的OutOfMemoryError(简称OOM)视为一种症状。但与任何疾病一样,并非所有OOM都意味着内存泄漏:由于生成大量局部变量或其他此类事件,OOM可能会发生。另一方面,并非所有内存泄漏都必然表现为OOM,特别是在桌面应用程序或客户端应用程序(没有重新启动时运行很长时间)的情况下。 将内存泄漏视为疾病,将OutOfMemoryError视为症状。但并非所有OutOfMemoryErrors都意味着内存泄漏,并非所有内存泄漏都表现为OutOfMemoryErrors。 为什么这些泄漏如此糟糕?除此之外,程序执行期间泄漏的内存块通常会降低系统性能,因为分配但未使用的内存块必须在系统耗尽空闲物理内存时进行换出。最终,程序甚至可能耗尽其可用的虚拟地址空间,从而导致OOM。 2.解密OutOfMemoryError 如上所述,OOM是内存泄漏的常见指示。实质上,当没有足够的空间来分配新对象时,会抛出错误。当垃圾收集器找不到必要的空间,并且堆不能进一步扩展,会多次尝试。因此,会出现错误以及堆栈跟踪。 诊断OOM的第一步是确定错误的实际含义。这听起来很清除,但答案并不总是那么清晰。例如:OOM是否是因为Java堆已满而出现,还是因为本机堆已满?为了帮助您回答这个问题,让我们分析一些可能的错误消息: java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: PermGen space java.lang.OutOfMemoryError: Requested array size exceeds VM limit java.lang.OutOfMemoryError: request bytes for . Out of swap space? java.lang.OutOfMemoryError: (Native method) 2.1.“Java heap space” 此错误消息不一定意味着内存泄漏。实际上,问题可能与配置问题一样简单。 例如,我负责分析一直产生这种类型的OutOfMemoryError的应用程序。经过一番调查后,我发现罪魁祸首是阵列实例化,因为需要太多的内存;在这种情况下,并不是应用程序的错,而是应用程序服务器依赖于默认的堆太小了。我通过调整JVM的内存参数解决了这个问题。 在其他情况下,特别是对于长期存在的应用程序,该消息可能表明我们无意中持有对象的引用,从而阻止垃圾收集器清理它们。这时Java语言等同于内存泄漏。 (注意:应用程序调用的API也可能无意中持有对象引用。) 这些“Java堆空间”OOM的另一个潜在来源是使用finalizers。如果类具有finalize方法,则在垃圾收集时该类型的对象不会被回收。而是在垃圾收集之后,稍后对象将排队等待最终确定。在Sun实现中,finalizers由守护线程执行。如果finalizers线程无法跟上finalization队列,那么Java堆可能会填满并且可能抛出OOM。 2.2.“PermGen space” 此错误消息表明永久代已满。永久代是存储类和方法对象的堆的区域。如果应用程序加载了大量类,则可能需要使用-XX:MaxPermSize选项增加永久代的大小。 Interned java.lang.String对象也存储在永久代中。 java.lang.String类维护一个字符串池。调用实习方法时,该方法检查池以查看是否存在等效字符串。如果是这样,它由实习方法返回;如果没有,则将字符串添加到池中。更准确地说,java.lang.String.intern方法返回一个字符串的规范表示;结果是对该字符串显示为文字时将返回的同一个类实例的引用。如果应用程序实例化大量字符串,则可能需要增加永久代的大小。 注意:您可以使用jmap -permgen命令打印与永久生成相关的统计信息,包括有关内部化String实例的信息。 2.3.“Requested array size exceeds VM limit” 此错误表示应用程序(或该应用程序使用的API)尝试分配大于堆大小的数组。例如,如果应用程序尝试分配512MB的数组但最大堆大小为256MB,则将抛出此错误消息的OOM。在大多数情况下,问题是配置问题或应用程序尝试分配海量数组时导致的错误。 2.4.“Request bytes for . Out of swap space?” 此消息似乎是一个OOM。但是,当本机堆的分配失败并且本机堆可能将被耗尽时,HotSpot VM会抛出此异常。消息中包括失败请求的大小(以字节为单位)以及内存请求的原因。在大多数情况下,是报告分配失败的源模块的名称。 如果抛出此类型的OOM,则可能需要在操作系统上使用故障排除实用程序来进一步诊断问题。在某些情况下,问题甚至可能与应用程序无关。例如,您可能会在以下情况下看到此错误: 操作系统配置的交换空间不足。 系统上的另一个进程是消耗所有可用的内存资源。 由于本机泄漏,应用程序也可能失败(例如,如果某些应用程序或库代码不断分配内存但无法将其释放到操作系统)。 2.5. (Native method) 如果您看到此错误消息并且堆栈跟踪的顶部框架是本机方法,则该本机方法遇到分配失败。此消息与上一个消息之间的区别在于,在JNI或本机方法中检测到Java内存分配失败,而不是在Java VM代码中检测到。 如果抛出此类型的OOM,您可能需要在操作系统上使用实用程序来进一步诊断问题。 2.6.Application Crash Without OOM 有时,应用程序可能会在从本机堆分配失败后很快崩溃。如果您运行的本机代码不检查内存分配函数返回的错误,则会发生这种情况。 例如,如果没有可用内存,malloc系统调用将返回NULL。如果未检查malloc的返回,则应用程序在尝试访问无效的内存位置时可能会崩溃。根据具体情况,可能很难定位此类问题。 在某些情况下,致命错误日志或崩溃转储的信息就足以诊断问题。如果确定崩溃的原因是某些内存分配中缺少错误处理,那么您必须找到所述分配失败的原因。与任何其他本机堆问题一样,系统可能配置了但交换空间不足,另一个进程可能正在消耗所有可用内存资源等。 3.泄漏诊断 在大多数情况下,诊断内存泄漏需要非常详细地了解相关应用程序。警告:该过程可能很长并且是迭代的。 我们寻找内存泄漏的策略将相对简单: 识别症状 启用详细垃圾回收 启用分析 分析踪迹 3.1 识别症状 正如所讨论的,在许多情况下,Java进程最终会抛出一个OOM运行时异常,这是一个明确的指示,表明您的内存资源已经耗尽。在这种情况下,您需要区分正常的内存耗尽和泄漏。分析OOM的消息并尝试根据上面提供的讨论找到罪魁祸首。 通常,如果Java应用程序请求的存储空间超过运行时堆提供的存储空间,则可能是由于设计不佳导致的。例如,如果应用程序创建映像的多个副本或将文件加载到数组中,则当映像或文件非常大时,它将耗尽存储空间。这是正常的资源耗尽。该应用程序按设计工作(虽然这种设计显然是愚蠢的)。 但是,如果应用程序在处理相同类型的数据时稳定地增加其内存利用率,则可能会发生内存泄漏。 3.2 启用详细垃圾收集 断言确实存在内存泄漏的最快方法之一是启用详细垃圾回收。通常可以通过检查verbosegc输出中的模式来识别内存约束问题。 具体来说,-verbosegc参数允许您在每次垃圾收集(GC)过程开始时生成跟踪。也就是说,当内存被垃圾收集时,摘要报告会打印到标准错误,让您了解内存的管理方式。 这是使用-verbosegc选项生成的一些典型输出: image 此GC跟踪文件中的每个块(或节)按递增顺序编号。要理解这种跟踪,您应该查看连续的分配失败节,并查找随着时间的推移而减少的释放内存(字节和百分比),同时总内存(此处,19725304)正在增加。这些是内存耗尽的典型迹象。 3.3 启用分析 不同的JVM提供了生成跟踪文件以反映堆活动的不同方法,这些方法通常包括有关对象类型和大小的详细信息。这称为分析堆。 3.4 分析路径 本文重点介绍Java VisualVM生成的跟踪。跟踪可以有不同的格式,因为它们可以由不同的Java内存泄漏检测工具生成,但它们背后的想法总是相同的:在堆中找到不应该存在的对象块,并确定这些对象是否累积而不是释放。特别感兴趣的是每次在Java应用程序中触发某个事件时已知的临时对象。应该仅存少量,但存在许多对象实例,通常表示应用程序出现错误。 最后,解决内存泄漏需要您彻底检查代码。了解对象泄漏的类型可能对此非常有用,并且可以大大加快调试速度。 4.垃圾收集如何在JVM中运行? 在我们开始分析具有内存泄漏问题的应用程序之前,让我们首先看看垃圾收集在JVM中的工作原理。 JVM使用一种称为跟踪收集器的垃圾收集器,它基本上通过暂停它周围的世界来操作,标记所有根对象(由运行线程直接引用的对象),并遵循它们的引用,标记它沿途看到的每个对象。 Java基于分代假设-实现了一种称为分代垃圾收集器的东西,该假设表明创建的大多数对象被快速丢弃,而未快速收集的对象可能会存在一段时间。 基于此假设,[Java将对象分为多代](www.oracle.com/technetwork…. Generations|outline)。这是一个视觉解释: image Young Generation -这是对象的开始。它有两个子代 Eden Space -对象从这里开始。大多数物体都是在Eden Space中创造和销毁的。在这里,GC执行Minor GCs,这是优化的垃圾收集。执行Minor GC时,对仍然需要的对象的任何引用都将迁移到其中一个survivors空间(S0或S1)。 Survivor Space (S0 and S1)-幸存Eden Space的对象最终来到这里。其中有两个,在任何给定时间只有一个正在使用(除非我们有严重的内存泄漏)。一个被指定为空,另一个被指定为活动,与每个GC循环交替。 Tenured Generation -也被称为老年代(图2中的旧空间),这个空间容纳存活较长的对象,使用寿命更长(如果它们活得足够长,则从Survivor空间移过来)。填充此空间时,GC会执行完整GC,这会在性能方面降低成本。如果此空间无限制地增长,则JVM将抛出OutOfMemoryError - Java堆空间。 Permanent Generation -作为与终身代密切相关的第三代,永久代是特殊的,因为它保存虚拟机所需的数据,以描述在Java语言级别上没有等价的对象。例如,描述类和方法的对象存储在永久代中。 Java足够聪明,可以为每一代应用不同的垃圾收集方法。使用名为Parallel New Collector的跟踪复制收集器处理年轻代。这个收集器阻止了这个世界,但由于年轻一代通常很小,所以暂停很短暂。 有关JVM代及其工作原理的更多信息,请查阅Memory Management in the Java HotSpot™ Virtual Machine 。 5 检测内存泄漏 要查找内存泄漏并消除它们,您需要合适的内存泄漏工具。是时候使用Java VisualVM检测并删除此类泄漏。 5.1 使用Java VisualVM远程分析堆 VisualVM是一种工具,它提供了一个可视化界面,用于查看有关基于Java技术的应用程序运行时的详细信息。 使用VisualVM,您可以查看与本地应用程序和远程主机上运行的应用程序相关的数据。您还可以捕获有关JVM软件实例的数据,并将数据保存到本地系统。 为了从Java VisualVM的所有功能中受益,您应该运行Java平台标准版(Java SE)版本6或更高版本。 Related: Why You Need to Upgrade to Java 8 Already 5.2. 为JVM启用远程连接 在生产环境中,通常很难访问运行代码的实际机器。幸运的是,我们可以远程分析我们的Java应用程序。 首先,我们需要在目标机器上授予自己JVM访问权限。为此,请使用以下内容创建名为jstatd.all.policy的文件: grant codebase "file:${java.home}/../lib/tools.jar" { permission java.security.AllPermission;
游客2q7uranxketok 2021-02-22 19:58:44 0 浏览量 回答数 0

问题

百问百答 《Java开发手册(嵩山版)》

从java命名标准来讲,代码中的命名需要注意什么? java中类名命名是用什么规则,有什么情形是例外的? POJO类中的布尔类型变量要不要加is前缀,为什么ÿ...
不语奈何 2021-03-25 13:30:32 28 浏览量 回答数 0

问题

C++面向对象【问答合集】

C++ 面向对象程序设计 异常处理 编程题https://yq.aliyun.com/ask/137471Dynamic_castC++类与对象问题继承和多态问题,求大神修改https://yq.aliyun.com/ask/10086c+...
马铭芳 2019-12-01 20:19:13 1780 浏览量 回答数 1

问题

【推荐】Windows虚拟内存不足问题的处理方法是什么

在 Windows 操作系统中,内核以及应用程序是通过虚拟内存来进行内存管理和使用。微软官方文章简单介绍了虚拟内存的概念。请参考: 什么是虚拟内存? 由于物理内存是有限的资源,Wind...
boxti 2019-12-01 22:06:24 3441 浏览量 回答数 0

回答

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

问题

10+年程序员总结的20+条经验教训

开发 1.从小事做起,然后再扩展 无论是创建一个新的系统,还是添加功能到现有的系统中,我总是从一个简单到几乎没有任何所需功能的版本启动,然后再一步一步地解决问题,直到...
雅蕾 2019-12-01 21:56:26 7714 浏览量 回答数 0

问题

一次小型流量攻击的检查和处理

这个案例中的客户用的并不是阿里云主机,但是对于阿里云可以作为一个典型的反面例子,因为阿里云可以为客户防御一定流量(貌似现在是5G)的攻击,这次案例中的流量对于阿里云实在算不了什么。 客户遇到...
云代维 2019-12-01 21:14:02 21742 浏览量 回答数 29

回答

从业余程序员到职业程序员 程序员刚入行时,我觉得最重要的是把自己培养成职业的程序员。 我的程序员起步比同龄人都晚了很多,更不用说现在的年轻人了。我大学读的是生物专业,在上大学前基本算是完全没接触过计算机。军训的时候因为很无聊,我和室友每天跑去学校的机房玩,我现在还印象很深刻,我第一次走进机房的时候,别人问,你是要玩windows,还是dos,我那是完全的一抹黑。后来就只记得在机房一堆人都是在练习盲打,军训完,盲打倒是练的差不多了,对计算机就这么产生了浓厚的兴趣,大一的时候都是玩组装机,捣鼓了一些,对计算机的硬件有了那么一些了解。 到大二后,买了一些书开始学习当时最火的网页三剑客,学会了手写HTML、PS的基本玩法之类的,课余、暑假也能开始给人做做网站什么的(那个时候做网站真的好赚钱),可能那样过了个一年左右,做静态的网页就不好赚钱了,也不好找实习工作,于是就开始学asp,写些简单的CRUD,做做留言板、论坛这些动态程序,应该算是在这个阶段接触编程了。 毕业后加入了深圳的一家做政府行业软件的公司,一个非常靠谱和给我空间的Leader,使得自己在那几年有了不错的成长,终于成了一个职业的程序员。 通常来说,业余或半职业的程序员,多数是1个人,或者很小的一个团队一起开发,使得在开发流程、协作工具(例如jira、cvs/svn/git等)、测试上通常会有很大的欠缺,而职业的程序员在这方面则会专业很多。另外,通常职业的程序员做的系统都要运行较长的时间,所以在可维护性上会特别注意,这点我是在加入阿里后理解更深的。一个运行10年的系统,和一个写来玩玩的系统显然是有非常大差别的。 这块自己感觉也很难讲清楚,只能说模模糊糊有个这样的概念。通常在有兴趣的基础上,从业余程序员跨越到成为职业程序员我觉得不会太难。 编程能力的成长 作为程序员,最重要的能力始终是编程能力,就我自己的感受而言,我觉得编程能力的成长主要有这么几个部分: 1、编程能力初级:会用 编程,首先都是从学习编程语言的基本知识学起的,不论是什么编程语言,有很多共同的基本知识,例如怎么写第一个Hello World、if/while/for、变量等,因此我比较建议在刚刚开始学一门编程语言的时候,看看编程语言自己的一些文档就好,不要上来就去看一些高阶的书。我当年学Java的时候上来就看Think in Java、Effective Java之类的,真心好难懂。 除了看文档以外,编程是个超级实践的活,所以一定要多写代码,只有这样才能真正熟练起来。这也是为什么我还是觉得在面试的时候让面试者手写代码是很重要的,这个过程是非常容易判断写代码的熟悉程度的。很多人会说由于写代码都是高度依赖IDE的,导致手写很难,但我绝对相信写代码写了很多的人,手写一段不太复杂的、可运行的代码是不难的。即使像我这种三年多没写过代码的人,让我现在手写一段不太复杂的可运行的Java程序,还是没问题的,前面N年的写代码生涯使得很多东西已经深入骨髓了。 我觉得编程能力初级这个阶段对于大部分程序员来说都不会是问题,勤学苦练,是这个阶段的核心。 2、编程能力中级:会查和避免问题 除了初级要掌握的会熟练的使用编程语言去解决问题外,中级我觉得首先是提升查问题的能力。 在写代码的过程中,出问题是非常正常的,怎么去有效且高效的排查问题,是程序员群体中通常能感受到的大家在编程能力上最大的差距。 解决问题能力强的基本很容易在程序员群体里得到很高的认可。在查问题的能力上,首先要掌握的是一些基本的调试技巧,好用的调试工具,在Java里有JDK自带的jstat、jmap、jinfo,不在JDK里的有mat、gperf、btrace等。工欲善其事必先利其器,在查问题上是非常典型的,有些时候大家在查问题时的能力差距,有可能仅仅是因为别人比你多知道一个工具而已。 除了调试技巧和工具外,查问题的更高境界就是懂原理。一个懂原理的程序员在查问题的水平上和其他程序员是有明显差距的。我想很多的同学应该能感受到,有些时候查出问题的原因仅仅是因为有效的工具,知其然不知其所以然。 我给很多阿里的同学培训过Java排查问题的方法,在这个培训里,我经常也会讲到查问题的能力的培养最主要的也是熟练,多尝试给自己写一些会出问题的程序,多积极的看别人是怎么查问题的,多积极的去参与排查问题,很多最后查问题能力强的人多数仅仅是因为“无他,但手熟尔”。 我自己排查问题能力的提升主要是在2009年和2010年。那两年作为淘宝消防队(处理各种问题和故障的虚拟团队)的成员,处理了很多的故障和问题。当时消防队还有阿里最公认的技术大神——多隆,我向他学习到了很多排查问题的技巧。和他比,我排查问题的能力就是初级的那种。 印象最深刻的是一次我们一起查一个应用cpu us高的问题,我们两定位到是一段代码在某种输入参数的时候会造成cpu us高的原因后,我能想到的继续查的方法是去生产环境抓输入参数,然后再用参数来本地debug看是什么原因。但多隆在看了一会那段代码后,给了我一个输入参数,我拿这个参数一运行,果然cpu us很高!这种case不是一次两次。所以我经常和别人说,我是需要有问题场景才能排查出问题的,但多隆是完全有可能直接看代码就能看出问题的,这是本质的差距。 除了查问题外,更厉害的程序员是在写代码的过程就会很好的去避免问题。大家最容易理解的就是在写代码时处理各种异常情况,这里通常也是造成程序员们之间很大的差距的地方。 写一段正向逻辑的代码,大部分情况下即使有差距,也不会太大,但在怎么很好的处理这个过程中有可能出现的异常上,这个时候的功力差距会非常明显。很多时候一段代码里处理异常逻辑的部分都会超过正常逻辑的代码量。 我经常说,一个优秀程序员和普通程序员的差距,很多时候压根就不需要看什么满天飞的架构图,而只用show一小段的代码就可以。 举一个小case大家感受下。当年有一个严重故障,最后查出的原因是输入的参数里有一个是数组,把这个数组里的值作为参数去查数据库,结果前面输入了一个很大的数组,导致从数据库查了大量的数据,内存溢出了,很多程序员现在看都会明白对入参、出参的保护check,但类似这样的case我真的碰到了很多。 在中级这个阶段,我会推荐大家尽可能的多刻意的去培养下自己这两个方面的能力,成为一个能写出高质量代码、有效排查问题的优秀程序员。 3、编程能力高级:懂高级API和原理 就我自己的经历而言,我是在写了多年的Java代码后,才开始真正更细致的学习和掌握Java的一些更高级的API,我相信多数Java程序员也是如此。 我算是从2003年开始用Java写商业系统的代码,但直到在2007年加入淘宝后,才开始非常认真地学习Java的IO通信、并发这些部分的API。尽管以前也学过也写过一些这样的代码,但完全就是皮毛。当然,这些通常来说有很大部分的原因会是工作的相关性,多数的写业务系统的程序员可能基本就不需要用到这些,所以导致会很难懂这些相对高级一些的API,但这些API对真正的理解一门编程语言,我觉得至关重要。 在之前的程序员成长路线的文章里我也讲到了这个部分,在没有场景的情况下,只能靠自己去创造场景来学习好。我觉得只要有足够的兴趣,这个问题还是不大的,毕竟现在有各种开源,这些是可以非常好的帮助自己创造机会学习的,例如学Java NIO,可以自己基于NIO包一个框架,然后对比Netty,看看哪些写的是不如Netty的,这样会非常有助于真正的理解。 在学习高级API的过程中,以及排查问题的过程中,我自己越来越明白懂编程语言的运行原理是非常重要的,因此我到了后面的阶段开始学习Java的编译机制、内存管理、线程机制等。对于我这种非科班出身的而言,学这些会因为缺乏基础更难很多,但这些更原理性的东西学会了后,对自己的编程能力会有质的提升,包括以后学习其他编程语言的能力,学这些原理最好的方法我觉得是先看看一些讲相关知识的书,然后去翻看源码,这样才能真正的更好的掌握,最后是在以后写代码的过程中、查问题的过程中多结合掌握的原理,才能做到即使在N年后也不会忘。 在编程能力的成长上,我觉得没什么捷径。我非常赞同1万小时理论,在中级、高级阶段,如果有人指点或和优秀的程序员们共事,会好非常多。不过我觉得这个和读书也有点像,到了一定阶段后(例如高中),天分会成为最重要的分水岭,不过就和大部分行业一样,大部分的情况下都还没到拼天分的时候,只需要拼勤奋就好。 系统设计能力的成长 除了少数程序员会进入专深的领域,例如Linux Kernel、JVM,其他多数的程序员除了编程能力的成长外,也会越来越需要在系统设计能力上成长。 通常一个编程能力不错的程序员,在一定阶段后就会开始承担一个模块的工作,进而承担一个子系统、系统、跨多领域的更大系统等。 我自己在工作的第三年开始承担一个流程引擎的设计和实现工作,一个不算小的系统,并且也是当时那个项目里的核心部分。那个阶段我学会了一些系统设计的基本知识,例如需要想清楚整个系统的目标、模块的划分和职责、关键的对象设计等,而不是上来就开始写代码。但那个时候由于我是一个人写整个系统,所以其实对设计的感觉并还没有那么强力的感觉。 在那之后的几年也负责过一些系统,但总体感觉好像在系统设计上的成长没那么多,直到在阿里的经历,在系统设计上才有了越来越多的体会。(点击文末阅读原文,查看:我在系统设计上犯过的14个错,可以看到我走的一堆的弯路)。 在阿里有一次做分享,讲到我在系统设计能力方面的成长,主要是因为三段经历,负责专业领域系统的设计 -> 负责跨专业领域的专业系统的设计 -> 负责阿里电商系统架构级改造的设计。 第一段经历,是我负责HSF。HSF是一个从0开始打造的系统,它主要是作为支撑服务化的框架,是个非常专业领域的系统,放在整个淘宝电商的大系统来看,其实它就是一个很小的子系统,这段经历里让我最深刻的有三点: 1).要设计好这种非常专业领域的系统,专业的知识深度是非常重要的。我在最早设计HSF的几个框的时候,是没有设计好服务消费者/提供者要怎么和现有框架结合的,在设计负载均衡这个部分也反复了几次,这个主要是因为自己当时对这个领域掌握不深的原因造成的; 2). 太技术化。在HSF的阶段,出于情怀,在有一个版本里投入了非常大的精力去引进OSGi以及去做动态化,这个后来事实证明是个非常非常错误的决定,从这个点我才真正明白在设计系统时一定要想清楚目标,而目标很重要的是和公司发展阶段结合; 3). 可持续性。作为一个要在生产环境持续运行很多年的系统而言,怎么样让其在未来更可持续的发展,这个对设计阶段来说至关重要。这里最low的例子是最早设计HSF协议的时候,协议头里竟然没有版本号,导致后来升级都特别复杂;最典型的例子是HSF在早期缺乏了缺乏了服务Tracing这方面的设计,导致后面发现了这个地方非常重要后,全部落地花了长达几年的时间;又例如HSF早期缺乏Filter Chain的设计,导致很多扩展、定制化做起来非常不方便。 第二段经历,是做T4。T4是基于LXC的阿里的容器,它和HSF的不同是,它其实是一个跨多领域的系统,包括了单机上的容器引擎,容器管理系统,容器管理系统对外提供API,其他系统或用户通过这个来管理容器。这个系统发展过程也是各种犯错,犯错的主要原因也是因为领域掌握不深。在做T4的日子里,学会到的最重要的是怎么去设计这种跨多个专业领域的系统,怎么更好的划分模块的职责,设计交互逻辑,这段经历对我自己更为重要的意义是我有了做更大一些系统的架构的信心。 第三段经历,是做阿里电商的异地多活。这对我来说是真正的去做一个巨大系统的架构师,尽管我以前做HSF的时候参与了淘宝电商2.0-3.0的重大技术改造,但参与和自己主导是有很大区别的,这个架构改造涉及到了阿里电商众多不同专业领域的技术团队。在这个阶段,我学会的最主要的: 1). 子系统职责划分。在这种超大的技术方案中,很容易出现某些部分的职责重叠和冲突,这个时候怎么去划分子系统,就非常重要了。作为大架构师,这个时候要从团队的职责、团队的可持续性上去选择团队; 2). 大架构师最主要的职责是控制系统风险。对于这种超大系统,一定是多个专业领域的架构师和大架构师共同设计,怎么确保在执行的过程中对于系统而言最重要的风险能够被控制住,这是我真正的理解什么叫系统设计文档里设计原则的部分。 设计原则我自己觉得就是用来确保各个子系统在设计时都会遵循和考虑的,一定不能是虚的东西,例如在异地多活架构里,最重要的是如何控制数据风险,这个需要在原则里写上,最基本的原则是可接受系统不可用,但也要保障数据一致,而我看过更多的系统设计里设计原则只是写写的,或者千篇一律的,设计原则切实的体现了架构师对目标的理解(例如当时异地多活这个其实开始只是个概念,但做到什么程度才叫做到异地多活,这是需要解读的,也要确保在技术层面的设计上是达到了目标的),技术方案层面上的选择原则,并确保在细节的设计方案里有对于设计原则的承接以及执行; 3). 考虑问题的全面性。像异地多活这种大架构改造,涉及业务层面、各种基础技术层面、基础设施层面,对于执行节奏的决定要综合考虑人力投入、机器成本、基础设施布局诉求、稳定性控制等,这会比只是做一个小的系统的设计复杂非常多。 系统设计能力的成长,我自己觉得最重要的一是先在一两个技术领域做到专业,然后尽量扩大自己的知识广度。例如除了自己的代码部分外,还应该知道具体是怎么部署的,部署到哪去了,部署的环境具体是怎么样的,和整个系统的关系是什么样的。 像我自己,是在加入基础设施团队后才更加明白有些时候软件上做的一个决策,会导致基础设施上巨大的硬件、网络或机房的投入,但其实有可能只需要在软件上做些调整就可以避免,做做研发、做做运维可能是比较好的把知识广度扩大的方法。 第二点是练习自己做tradeoff的能力,这个比较难,做tradeoff这事需要综合各种因素做选择,但这也是所有的架构师最关键的,可以回头反思下自己在做各种系统设计时做出的tradeoff是什么。这个最好是亲身经历,听一些有经验的架构师分享他们选择背后的逻辑也会很有帮助,尤其是如果恰好你也在同样的挑战阶段,光听最终的架构结果其实大多数时候帮助有限。 技术Leader我觉得最好是能在架构师的基础上,后续注重成长的方面还是有挺大差别,就不在这篇里写了,后面再专门来写一篇。 程序员金字塔 我认为程序员的价值关键体现在作品上,被打上作品标签是一种很大的荣幸,作品影响程度的大小我觉得决定了金字塔的层次,所以我会这么去理解程序员的金字塔。 当然,要打造一款作品,仅有上面的两点能力是不够的,作品里很重要的一点是对业务、技术趋势的判断。 希望作为程序员的大伙,都能有机会打造一款世界级的作品,去为技术圈的发展做出贡献。 由于目前IT技术更新速度还是很快的,程序员这个行当是特别需要学习能力的。我一直认为,只有对程序员这个职业真正的充满兴趣,保持自驱,才有可能在这个职业上做好,否则的话是很容易淘汰的。 作者简介: 毕玄,2007年加入阿里,十多年来主要从事在软件基础设施领域,先后负责阿里的服务框架、Hbase、Sigma、异地多活等重大的基础技术产品和整体架构改造。
茶什i 2020-01-10 15:19:35 0 浏览量 回答数 0

回答

更换服务器~100个是单服务器最大的负荷了你用的是镶嵌式的,要选择服务器机组的那种~刀片式服务器~然后oracl数据库支持分开安装。同步处理~ 你肯定买的是架式服务器~######装ORACLE服务器是刀片式的,6核至强 24G的内存 应该不是服务器瓶颈######oracl装在独立的一台服务器上的话,只支持小形企业和地、市级企业运行 你说的情况,可以理解你的数据量非常庞大,,有可能是省、国家级的数据量了~~ 让你单位给你单独开个服务器房间,更换服务器机柜然后购买刀片式服务器做服务器阵列机组~######数据量倒不会太大,一天1G不到,问题是很多存储过程的逻辑很复杂,一条线程调用存储过程,要等待很久才会返回,直接导致工作线程速度很慢,数据进入速度太快,工作异常状态频繁出现。######必须要实时的存入数据库吗?不能先缓存到服务器,然后让服务器慢慢去处理吗?或者直接将数据记入日志,然后sqlload?######回复 @xinzaibing : 我想到一个蛋疼的方式:数据写文件,文件内容定期入库,程序定期读取数据库计算的结果缓存到内存中。不知道你具体需求,瞎琢磨一个。######回复 @asdfsx : 公司领导一致认为内存不可靠,断电、程序异常什么的...存在内存的数据就没了...真是蛋疼啊######回复 @xinzaibing : 如果数据量不大的话,还有一个方案就是都保存在内存里,然后定时把内存里的结果同步到数据库里。数据库的逻辑挪到程序里..........这个方案比较累啊。另外就是缓存可以加个优先级高低的判断。######目前要求是必须要实时入库,采取写日志文件的方法也可以。 这些数据有一个特点,在某一个时刻会有一个突然出现的峰值,然后又慢慢变少,但是这个时间是不固定的,由于只实用了一条双缓冲队列,所有需要紧急处理的数据和非紧急处理的数据都在队列里,而如果遇到非紧急数据,处理了很长的时间,就直接导致后面的紧急数据失效了...或者导致嵌入式程序判断服务端未收到数据,进而采取重发,导致一条队列里有非常多重复的数据。######我可能会使用数据写入日志文件,然后定时将日志入库的办法操作######大概意思可能是多线程对数据库表的操作导致数据表锁定,性能损失在内耗上了。。那数据表采用行级锁呢?(这样会增大系统开销)我是菜鸟,求教  ######回复 @xinzaibing : 这个应该是属于最初的设计问题,hohoho######回复 @asdfsx : 目前我也在往这方面考虑,如果数据分类处理。那就得大改结构了...唉######回复 @xinzaibing : 建议根据上传的不同数据进行不同的处理,不要一股脑的都放在缓存中,如果是心跳的话,应该立即响应,如果是要处理的数据的话,才需要进行缓存等待处理######ORACLE默认就是行级锁的应该.. 主要是数据的写入速度远远小于数据上传的速度,导致了缓存溢出,紧急数据不能得到及时处理,大量数据出现超时失效,无法对嵌入式的采集器程序作出及时的心跳相应和其他回复(因为都在队列中,无法处理,无心跳的话嵌入式采集器会误认为服务器断线)。最终导致单台服务器接入数据的嵌入式设备的数量太少,不满足需求。######去年刚毕业,由于公司小,一个人搞后台,压力太大啊...大家指指招呗~ @中山野鬼######今天到图书馆看了一本书《让Orcale跑的更快点》,上面说可以从如下几个方面优化: 数据库方面:建适当的索引,固定长度;查询条件比较尽量简化;不同的表放在不同的磁盘里…… 服务层:增大缓存,(有没有数据库连接池不知道你能用上不) 软件层:对Java使用PaperStatement 囫囵吞枣就记得这么多了。。。哭~~######非常感谢...我去看看这本书 :)######我不清楚你的数据采集的内容是什么。不过看的出,对实时性要求高。换我,基本上就一个思路。 1、做个前段服务器,什么事情都不干,只进行数据的压缩。然后所有数据库和计算操作,放到后端。 至于并发,你这种 1W=100台服务器的方式治标不治本。######@中山野鬼 是说对数据进行预处理,提取有效内容?还是就是zip?######回复 @asdfsx : 不一样的。而是数据压缩。采样数据中间,信息密度不会太大的。######老鬼的思路有点像我说的那个数据写日志文件,或者内存缓存定时入库...........都被否定了啊######@xinzaibing 还有一个建议,上传的数据加一个验证,如果上传的数据已经插入缓存,就不要再次插入了。无脑插入插到崩也不是什么好主意啊######回复 @asdfsx : 要回复的,要处理成功后才回复,存库失败或者某些异常导致服务端崩溃重启,就不进行回复,客户端会持续地进行重发,重发到一定次数后,存本地,等恢复正常后发送存本地的数据
kun坤 2020-06-09 11:56:38 0 浏览量 回答数 0

回答

Kotlin的简介 Kotlin是由JetBrains公司(IDEA开发者)所开发的编程语言,其名称来自于开发团队附近的科特林岛。 多平台开发 JVM :Android; Server-Side Javascript:前端 Native(beta) :开发原生应用 windows、macos、linux Swift与Kotlin非常像 http://nilhcem.com/swift-is-like-kotlin/ kotlin发展历程 image.png java发展历程 image.png JVM语言的原理 image.png JVM规范与java规范是相互独立的 只要生成的编译文件匹配JVM字节码规范,任何语言都可以由JVM编译运行. Kotlin也是一种JVM语言,完全兼容java,可以与java相互调用;Kotlin语言的设计受到Java、C#、JavaScript、Scala、Groovy等语言的启发 kotlin的特性 下面不会罗列kotlin中具体的语法,会介绍我认为比较重要的特性,以及特性背后的东西。 类型推断 空类型设计 函数式编程 类型推断 image.png 类型推断是指编程语言中在编译期自动推导出值的数据类型。推断类型的能力让很多编程任务变得容易,让程序员可以忽略类型标注的同时仍然允许类型检查。 在开发环境中,我们往往写出表达式,然后可以用快捷键来生成变量声明,往往都是很准的,这说明了编译器其实是可以很准确的推断出来类型的。编程语言所具备的类型推断能力可以把类型声明的任务由开发者转到了编译器. java中声明变量的方式是类型写在最前面,后面跟着变量名,这就迫使开发者在声明变量时就要先思考变量的类型要定义成什么,而在一些情况下比如使用集合、泛型类型的变量,定义类型就会变得比较繁琐。 Kotlin中声明变量,类型可以省略,或者放到变量名后面,这可以降低类型的权重,从必选变为可选,降低开发者思维负担。java10中也引入了类型推断。 Javascript中声明变量也是用关键字var,但是还是有本质区别的,Kotlin中的类型推断并不是变成动态类型、弱类型,类型仍然是在编译期就已经决定了的,Kotlin仍然是静态类型、强类型的编程语言。javascript由于是弱类型语言,同一个变量可以不经过强制类型转换就被赋不同数据类型的值, 编程语言的一个趋势就是抽象程度越来越高,编译器做更多的事情。 空类型设计 空类型的由来 image.png 托尼·霍尔(Tony Hoare),图灵奖得主 托尼·霍尔是ALGOL语言的设计者,该语言在编程语言发展历史上非常重要,对其他编程语言产生重大影响,大多数近代编程语言(包括C语言)皆使用类似ALGOL的语法。他在一次大会上讨论了null应用的设计: “我把 null 引用称为自己的十亿美元错误。它的发明是在1965 年,那时我用一个面向对象语言( ALGOL W )设计了第一个全面的引用类型系统。我加入了null引用设计,仅仅是因为实现起来非常容易。它导致了数不清的错误、漏洞和系统崩溃,可能在之后 40 年中造成了十亿美元的损失。” null引用存在的问题 以java为例,看null引用的设计到底存在哪些问题 空指针问题NPE 编译时不能对空指针做出检查,运行时访问null对象就会出现错误,这个就是工程中常见的空指针异常。 null本身没有语义,会存在歧义 值未被初始化 值不存在 也许表示一种状态 逻辑上有漏洞 Java中,null可以赋值给任何引用,比如赋值给String类型变量,String a = null,但是null并不是String类型: a instanceof String 返回的是false,这个其实是有些矛盾的。所以当持有一个String类型的变量,就存在两种情况,null或者真正的String. 解决NPE的方式 防御式代码 在访问对象前判空,但会有冗余代码;会规避问题,而隐藏真正的问题 抛出异常给调用方处理 方法中传参传入的空值、无效值,抛出受检查异常给上层调用方 增加注解 Android中可以增加@NonNull注解,编译时做额外检查 空状态对象设计模式 空状态对象是一个实现接口但是不做任何业务逻辑的对象,可以取代判空检查;这样的空状态对象也可以在数据不可用的时候提供默认的行为 java8 Optional类 java8中引入了Optional类,来解决广泛存在的null引用问题.官方javadoc文档介绍 A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value. Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present). 来看一下是如何实现的。 举一个访问对象读取熟悉的例子 java 8 之前 : image.png java 8: image.png 总结: 1.用Optional还是会比较繁琐,这个也说明了设计一个替代null的方案还是比较难的。 optional的耗时大约是普通判空的数十倍,主要是涉及泛型、使用时多创键了一个对象的创建;数据比较大时,会造成性能损失。 java8 引入Optional的意义在于提示调用者,用特殊类型包装的变量可能为空,在使用取出时需要判断 Kotlin的空类型设计 Kotlin中引入了可空类型和不可空类型的区分,可以区分一个引用可以容纳null,还是不能容纳null。 String vs String? String 类型表示变量不能为空,String?则表示变量可以为空 String?含义是String or null.这两种是不同的类型. 比如: var a:String = “abc” //ok var a:String = null //不允许 var b :String? = null //ok a=b // 不允许 String?类型的值不能给String类型的值赋值 这样就将类型分成了可空类型和不可能类型,每一个类型都有这样的处理;Kotlin中访问非空类型变量永远不会出现空指针异常。 同样上面的例子,采用Kotlin去写,就会简洁很多 image.png 编程范式-函数式编程 编程范式是什么? 编程范式是程序员看待程序和写程序的观点 主要的类型 非结构化编程 结构化编程 面向对象编程 命令式编程 函数式编程 这些类型并不是彼此互斥的,而是按照不同的维度做的划分,一种编程语言可能都支持多个编程范式 非结构化编程 第一代的高级语言往往是非结构化编程 比如 BASIC语言 每一行的代码前面都有一个数字作为行号,通常使用GOTO的跳跃指令来实现判断和循环. 看一下下面这段代码是做什么的: image.png 实际上做的是:程序在屏幕上显示数字 1 到 10 及其对应的平方 采用这种方式写程序,大量的使用goto实现逻辑的跳转,代码一长,可读性和维护性就比较差了,形成“面条式代码” 结构化编程 采用顺序、分支、循环结构来表达,禁用或者少用GOTO; 并用子程序来组织代码,采用自顶向下的方式来写程序 代表语言是C语言 实现同样的逻辑: image.png 可见采用结构化编程,代码的逻辑会更清晰。 面向对象编程 思想: 将计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。 特性: 封装性、继承性、多态性。 命令式编程 把计算机程序视为一系列的命令集合 主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。 “先做这,再做那”,强调“怎么做” 实现: 用变量来储存数据,用语句来执行指令,改变变量状态。 基本所有的常见的编程语言都具有此范式 函数式编程 声明式语法,描述要什么,而不是怎么做 类似于SQL语句 语言: kotlin swift python javascript scala 函数是第一等公民 可以赋值给变量,可作为参数传入另一个函数,也可作为函数的返回值 纯函数 y=f(x) 只要输入相同,返回值不变 没有副作用:不修改函数的外部状态 举个栗子 公司部门要进行outing,去哪里是个问题,要考虑多个因素,比如花费、距离、天数等等,有多个备选地点进行选择。 定义一个数据类: image.png 要进行筛选了,分别用sql,kotlin,java来实现 找出花费低于2000元的outing地点信息 SQL image.png Kotlin image.png java 7 image.png 可见kotin的写法还是比较接近于sql的思想的,声明式的写法,而不管具体如何实现;其中的:place->place.money<2000 就是函数,可以作为参数传递给fliter这个高阶函数;而且这个函数没有副作用,不改变外部状态。 再来一个复杂一点的: 找出花费低于5000元,时间不多于4天,按照距离排序的outing地点名称 SQL image.png Kotlin: image.png java 7 image.png 由此可见用kotlin的函数式写法,会更简洁,逻辑也更清晰,这段代码的目标一目了然,这种清晰在于实现了业务逻辑与控制逻辑的分离,业务逻辑就是由函数实现的,比如place->place.money<500,而控制逻辑是由filter,sorterBy等高阶函数实现的。 而java的传统写法是基于对数据的操作,避免不了遍历的操作,业务逻辑与控制逻辑交织在了一起,这段代码的目的就不是那么容易清晰看到的了。 总结 kotlin是实用的现代编程语言,吸收了众多编程语言的优点,支持类型推断、空类型安全、函数式编程、DSL等特性,非常值得学习和使用。
问问小秘 2020-04-30 16:33:40 0 浏览量 回答数 0

问题

【开源项目】Seata问答集锦

当发现必须延迟实例化的bean的目标类时,Seata GlobalTransactionScanner.wrapIfNecessary触发BeanInstantiationExceptionSpringBoot与 Seata ...
一人吃饱,全家不饿 2021-02-02 11:31:44 2 浏览量 回答数 0

问题

使用Hibernate的ScrollableResults缓慢读取9000万条记录?mysql

我只需要使用Hibernate读取MySQL数据库中表中的每一行,并基于它编写一个文件。但是有9000万行,它们很大。因此,似乎以下情况将是适当的: ScrollableResult...
保持可爱mmm 2020-05-17 10:15:00 1 浏览量 回答数 1

问题

云计算定义与诠释矛盾与纠结

       现如今,在云时代下,云计算的应用已经深入到各个领域,与此同时,云计算已经成为一个更为确定的平台,现在我们看到比以往更多的案例越来越多的企业开始瞄准云计算模型...
西瓜小狼 2019-12-01 22:08:15 8736 浏览量 回答数 6

问题

Swift2.0到底「新」在哪?

【编者按】2015年6月,一年一度的苹果 WWDC 大会如期而至,在大会上苹果发布了 Swift 2.0,引入了很多新的特性,以帮助开发者更快、更简单地构建应用。本篇文章作者是 Max...
忆远0711 2019-12-01 21:30:17 8367 浏览量 回答数 0

问题

【开源项目】Nacos问答集锦

nacos 如果注册到不同的命名空间下,如何相互调用呢使用nacos-server-1.0.0时出现日志不兼容情况,请问是什么原因导致的nacos-server 空配置报错,java.util.Co...
一人吃饱,全家不饿 2021-02-02 10:51:08 11 浏览量 回答数 0

问题

ECS Windows 系统 SVCHOST.EXE进程占用资源(CPU,内存)较高的处理是什么

问题描述 ECS Windows系统内名为【svchost.exe】的进程,CPU或内存资源使用率一直居高不下,导致系统卡顿,影响正常使用。 什么是 svchost.exesvchost.exe 是...
boxti 2019-12-01 22:06:56 2057 浏览量 回答数 0

问题

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

为了方便Java开发者快速找到相关技术问题和答案,开发者社区策划了Java技术1000问内容,包含最基础的如何学Java、实践中遇到的技术问题、RocketMQ面试、Java容器部署实践等维度内容。 我们会以每...
问问小秘 2019-12-01 21:57:43 46087 浏览量 回答数 16

问题

JDBC 使用经验之谈 为什么选择JDBC JDBC使用经验:报错

 written by chenshu,郭春晓 为什么选择JDBC SSH架构在中国很流行,可是很多人都觉得Hibernate太慢了,所以有了MyBatis.从数据库开发技术上,EJB3大幅度借...
kun坤 2020-06-09 22:12:13 0 浏览量 回答数 1

回答

如果对什么是线程、什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内。 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。 很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程 并行与并发: 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。 并发与并行 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码: void transferMoney(User from, User to, float amount){ to.setMoney(to.getBalance() + amount); from.setMoney(from.getBalance() - amount); } 同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。 好了,让我们开始吧。我准备分成几部分来总结涉及到多线程的内容: 扎好马步:线程的状态 内功心法:每个对象都有的方法(机制) 太祖长拳:基本线程类 九阴真经:高级多线程控制类 扎好马步:线程的状态 先来两张图: 线程状态 线程状态转换 各种状态一目了然,值得一提的是"blocked"这个状态:线程在Running的过程中可能会遇到阻塞(Blocked)情况 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable) 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。 此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。内功心法:每个对象都有的方法(机制) synchronized, wait, notify 是任何对象都具有的同步工具。让我们先来了解他们 monitor 他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确monitor的概念,Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。 wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。 当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。 再讲用法: synchronized单独使用: 代码块:如下,在多线程环境下,synchronized块中的方法获取了lock实例的monitor,如果实例相同,那么只有一个线程能执行该块内容 复制代码 public class Thread1 implements Runnable { Object lock; public void run() { synchronized(lock){ ..do something } } } 复制代码 直接用于方法: 相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的是static方法,则锁定该类所有实例。 public class Thread1 implements Runnable { public synchronized void run() { ..do something } } synchronized, wait, notify结合:典型场景生产者消费者问题 复制代码 /** * 生产者生产出来的产品交给店员 */ public synchronized void produce() { if(this.product >= MAX_PRODUCT) { try { wait(); System.out.println("产品已满,请稍候再生产"); } catch(InterruptedException e) { e.printStackTrace(); } return; } this.product++; System.out.println("生产者生产第" + this.product + "个产品."); notifyAll(); //通知等待区的消费者可以取出产品了 } /** * 消费者从店员取产品 */ public synchronized void consume() { if(this.product <= MIN_PRODUCT) { try { wait(); System.out.println("缺货,稍候再取"); } catch (InterruptedException e) { e.printStackTrace(); } return; } System.out.println("消费者取走了第" + this.product + "个产品."); this.product--; notifyAll(); //通知等待去的生产者可以生产产品了 } 复制代码 volatile 多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。 volatile 针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。太祖长拳:基本线程类 基本线程类指的是Thread类,Runnable接口,Callable接口Thread 类实现了Runnable接口,启动一个线程的方法:  MyThread my = new MyThread();  my.start(); Thread类相关方法:复制代码 //当前线程可转让cpu控制权,让别的就绪状态线程运行(切换)public static Thread.yield() //暂停一段时间public static Thread.sleep() //在一个线程中调用other.join(),将等待other执行完后才继续本线程。    public join()//后两个函数皆可以被打断public interrupte() 复制代码 关于中断:它并不像stop方法那样会中断一个正在运行的线程。线程会不时地检测中断标识位,以判断线程是否应该被中断(中断标识值是否为true)。终端只会影响到wait状态、sleep状态和join状态。被打断的线程会抛出InterruptedException。Thread.interrupted()检查当前线程是否发生中断,返回booleansynchronized在获锁的过程中是不能被中断的。 中断是一个状态!interrupt()方法只是将这个状态置为true而已。所以说正常运行的程序不去检测状态,就不会终止,而wait等阻塞方法会去检查并抛出异常。如果在正常运行的程序中添加while(!Thread.interrupted()) ,则同样可以在中断后离开代码体 Thread类最佳实践:写的时候最好要设置线程名称 Thread.name,并设置线程组 ThreadGroup,目的是方便管理。在出现问题的时候,打印线程栈 (jstack -pid) 一眼就可以看出是哪个线程出的问题,这个线程是干什么的。 如何获取线程中的异常 不能用try,catch来获取线程中的异常Runnable 与Thread类似Callable future模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是isDone和get。其中Future对象用来存放该线程的返回值以及状态 ExecutorService e = Executors.newFixedThreadPool(3); //submit方法有多重参数版本,及支持callable也能够支持runnable接口类型.Future future = e.submit(new myCallable());future.isDone() //return true,false 无阻塞future.get() // return 返回值,阻塞直到该线程运行结束 九阴真经:高级多线程控制类 以上都属于内功心法,接下来是实际项目中常用到的工具了,Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent, 提供了大量高级工具,可以帮助开发者编写高效、易维护、结构清晰的Java多线程程序。1.ThreadLocal类 用处:保存线程的独立变量。对一个线程类(继承自Thread)当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录session信息。 实现:每个Thread都持有一个TreadLocalMap类型的变量(该类是一个轻量级的Map,功能与map一样,区别是桶里放的是entry而不是entry的链表。功能还是一个map。)以本身为key,以目标为value。主要方法是get()和set(T a),set之后在map里维护一个threadLocal -> a,get时将a返回。ThreadLocal是一个特殊的容器。2.原子类(AtomicInteger、AtomicBoolean……) 如果使用atomic wrapper class如atomicInteger,或者使用自己保证原子的操作,则等同于synchronized //返回值为booleanAtomicInteger.compareAndSet(int expect,int update) 该方法可用于实现乐观锁,考虑文中最初提到的如下场景:a给b付款10元,a扣了10元,b要加10元。此时c给b2元,但是b的加十元代码约为:复制代码 if(b.value.compareAndSet(old, value)){ return ;}else{ //try again // if that fails, rollback and log} 复制代码 AtomicReference对于AtomicReference 来讲,也许对象会出现,属性丢失的情况,即oldObject == current,但是oldObject.getPropertyA != current.getPropertyA。这时候,AtomicStampedReference就派上用场了。这也是一个很常用的思路,即加上版本号3.Lock类  lock: 在java.util.concurrent包内。共有三个实现: ReentrantLockReentrantReadWriteLock.ReadLockReentrantReadWriteLock.WriteLock 主要目的是和synchronized一样, 两者都是为了解决同步问题,处理资源争端而产生的技术。功能类似但有一些区别。 区别如下:复制代码 lock更灵活,可以自由定义多把锁的枷锁解锁顺序(synchronized要按照先加的后解顺序)提供多种加锁方案,lock 阻塞式, trylock 无阻塞式, lockInterruptily 可打断式, 还有trylock的带超时时间版本。本质上和监视器锁(即synchronized是一样的)能力越大,责任越大,必须控制好加锁和解锁,否则会导致灾难。和Condition类的结合。性能更高,对比如下图: 复制代码 synchronized和Lock性能对比 ReentrantLock    可重入的意义在于持有锁的线程可以继续持有,并且要释放对等的次数后才真正释放该锁。使用方法是: 1.先new一个实例 static ReentrantLock r=new ReentrantLock(); 2.加锁       r.lock()或r.lockInterruptibly(); 此处也是个不同,后者可被打断。当a线程lock后,b线程阻塞,此时如果是lockInterruptibly,那么在调用b.interrupt()之后,b线程退出阻塞,并放弃对资源的争抢,进入catch块。(如果使用后者,必须throw interruptable exception 或catch)     3.释放锁    r.unlock() 必须做!何为必须做呢,要放在finally里面。以防止异常跳出了正常流程,导致灾难。这里补充一个小知识点,finally是可以信任的:经过测试,哪怕是发生了OutofMemoryError,finally块中的语句执行也能够得到保证。 ReentrantReadWriteLock 可重入读写锁(读写锁的一个实现)   ReentrantReadWriteLock lock = new ReentrantReadWriteLock()  ReadLock r = lock.readLock();  WriteLock w = lock.writeLock(); 两者都有lock,unlock方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码4.容器类 这里就讨论比较常用的两个: BlockingQueueConcurrentHashMap BlockingQueue阻塞队列。该类是java.util.concurrent包下的重要类,通过对Queue的学习可以得知,这个queue是单向队列,可以在队列头添加元素和在队尾删除或取出元素。类似于一个管  道,特别适用于先进先出策略的一些应用场景。普通的queue接口主要实现有PriorityQueue(优先队列),有兴趣可以研究 BlockingQueue在队列的基础上添加了多线程协作的功能: BlockingQueue 除了传统的queue功能(表格左边的两列)之外,还提供了阻塞接口put和take,带超时功能的阻塞接口offer和poll。put会在队列满的时候阻塞,直到有空间时被唤醒;take在队 列空的时候阻塞,直到有东西拿的时候才被唤醒。用于生产者-消费者模型尤其好用,堪称神器。 常见的阻塞队列有: ArrayListBlockingQueueLinkedListBlockingQueueDelayQueueSynchronousQueue ConcurrentHashMap高效的线程安全哈希map。请对比hashTable , concurrentHashMap, HashMap5.管理类 管理类的概念比较泛,用于管理线程,本身不是多线程的,但提供了一些机制来利用上述的工具做一些封装。了解到的值得一提的管理类:ThreadPoolExecutor和 JMX框架下的系统级管理类 ThreadMXBeanThreadPoolExecutor如果不了解这个类,应该了解前面提到的ExecutorService,开一个自己的线程池非常方便:复制代码 ExecutorService e = Executors.newCachedThreadPool(); ExecutorService e = Executors.newSingleThreadExecutor(); ExecutorService e = Executors.newFixedThreadPool(3); // 第一种是可变大小线程池,按照任务数来分配线程, // 第二种是单线程池,相当于FixedThreadPool(1) // 第三种是固定大小线程池。 // 然后运行 e.execute(new MyRunnableImpl()); 复制代码 该类内部是通过ThreadPoolExecutor实现的,掌握该类有助于理解线程池的管理,本质上,他们都是ThreadPoolExecutor类的各种实现版本。请参见javadoc: ThreadPoolExecutor参数解释 翻译一下:复制代码 corePoolSize:池内线程初始值与最小值,就算是空闲状态,也会保持该数量线程。maximumPoolSize:线程最大值,线程的增长始终不会超过该值。keepAliveTime:当池内线程数高于corePoolSize时,经过多少时间多余的空闲线程才会被回收。回收前处于wait状态unit:时间单位,可以使用TimeUnit的实例,如TimeUnit.MILLISECONDS workQueue:待入任务(Runnable)的等待场所,该参数主要影响调度策略,如公平与否,是否产生饿死(starving)threadFactory:线程工厂类,有默认实现,如果有自定义的需要则需要自己实现ThreadFactory接口并作为参数传入。 阿里云优惠券地址https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=nb3paa5b
景凌凯 2019-12-02 01:40:35 0 浏览量 回答数 0

问题

【精品问答】不懂如何使用ECS?ECS功能百问看这里

【精品问答】不懂如何使用ECS?ECS功能百问看这里 云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务࿰...
问问小秘 2020-01-02 15:48:11 9261 浏览量 回答数 5

云产品推荐

上海奇点人才服务相关的云产品 小程序定制 上海微企信息技术相关的云产品 国内短信套餐包 ECS云服务器安全配置相关的云产品 开发者问答 阿里云建站 自然场景识别相关的云产品 万网 小程序开发制作 视频内容分析 视频集锦 代理记账服务 阿里云AIoT