java web项目 如何减少java内存分配消耗,有什么工具

java中三种常见内存溢出错误的处理方法 - 为程序员服务
为程序员服务
java中三种常见内存溢出错误的处理方法
相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识。
在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识。jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。其中永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。堆区域用来存放Class的实例(即对象),对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。而Java栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。
第一种OutOfMemoryError: PermGen space
发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题有以下两种办法:
增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行: JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m" 如果是windows服务器还可以在系统环境变量中设置。感觉用tomcat发布sprint+struts+hibernate架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署ssh项目的tomcat服务器经常宕机的问题。
清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到tomcat共同的lib下,减少类的重复加载。这种方法是网上部分人推荐的,我没试过,但感觉减少不了太大的空间,最靠谱的还是第一种方法。
第二种OutOfMemoryError:
Java heap space
发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:
检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。 我以前写一个使用K-Means文本聚类算法对几万条文本记录(每条记录的特征向量大约10来个)进行文本聚类时,由于程序细节上有问题,就导致了Java heap space的内存溢出问题,后来通过修改程序得到了解决。
增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m
第三种OutOfMemoryError:unable to create new native thread
在java应用中,有时候会出现这样的错误:OutOfMemoryError: unable to create new native thread.这种怪事是因为JVM已经被系统分配了大量的内存(比如1.5G),并且它至少要占用可用内存的一半。有人发现,在线程个数很多的情况下,你分配给JVM的内存越多,那么,上述错误发生的可能性就越大。
那么是什么原因造成这种问题呢?
每一个32位的进程最多可以使用2G的可用内存,因为另外2G被操作系统保留。这里假设使用1.5G给JVM,那么还余下500M可用内存。这500M内存中的一部分必须用于系统dll的加载,那么真正剩下的也许只有400M,现在关键的地方出现了:当你使用Java创建一个线程,在JVM的内存里也会创建一个Thread对象,但是同时也会在操作系统里创建一个真正的物理线程(参考JVM规范),操作系统会在余下的400兆内存里创建这个物理线程,而不是在JVM的1500M的内存堆里创建。在jdk1.4里头,默认的栈大小是256KB,但是在jdk1.5里头,默认的栈大小为1M每线程,因此,在余下400M的可用内存里边我们最多也只能创建400个可用线程。
这样结论就出来了,要想创建更多的线程,你必须减少分配给JVM的最大内存。还有一种做法是让JVM宿主在你的JNI代码里边。
给出一个有关能够创建线程的最大个数的估算公式:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
对于jdk1.5而言,假设操作系统保留120M内存:
1.5GB JVM: (2GB-1.5Gb-120MB)/(1MB) = ~380 threads
1.0GB JVM: (2GB-1.0Gb-120MB)/(1MB) = ~880 threads
对于栈大小为256KB的jdk1.4而言,
1.5GB allocated to JVM: ~1520 threads
1.0GB allocated to JVM: ~3520 threads
对于这个异常我们首先需要判断下,发生内存溢出时进程中到底都有什么样的线程,这些线程是否是应该存在的,是否可以通过优化来降低线程数; 另外一方面默认情况下java为每个线程分配的栈内存大小是1M,通常情况下,这1M的栈内存空间是足足够用了,因为在通常在栈上存放的只是基础类型的数据或者对象的引用,这些东西都不会占据太大的内存, 我们可以通过调整jvm参数,降低为每个线程分配的栈内存大小来解决问题,例如在jvm参数中添加-Xss128k将线程栈内存大小设置为128k。
推荐阅读:
相关聚客文章java web项目 如何减少内存消耗,有什么工具可以分担内存消耗? - 知乎有问题,上知乎。知乎作为中文互联网最大的知识分享平台,以「知识连接一切」为愿景,致力于构建一个人人都可以便捷接入的知识分享网络,让人们便捷地与世界分享知识、经验和见解,发现更大的世界。8被浏览775分享邀请回答赞同 3 条评论分享收藏感谢收起赞同 添加评论分享收藏感谢收起写回答北京,2017,多少k的java web程序员应该懂多线程和jvm优化?
百姓大小事,一呼百应!
北京,2017,多少k的java web程序员应该懂多线程和jvm优化?
北京,2017,多少k的java web程序员应该懂多线程和jvm优化?
干java web一年半了,从来没深研究过多线程,jvm优化更是碰都没碰到过,准备跳槽,去面试问我这个有点懵, java web中线程不是由tomcat这类web容器负责的吗?为什么还要我去控制多线程? 什么样的web应用业务场景需要用到多线程通信,线程阻塞,线程锁? jvm优化是什么,什么样的业务场景又需要优化jvm。到底该如何优化,是优化代码,注意代码的的写法,还是有什么配置文件? 就知道web容器调个jvm运行内存程序运行会变快。 不懂…
这几天比较忙。加个插曲。前两天面试了一个四年工作经验的Java工程师,挺失望的。工作换了四五家,大多浅尝则止,基础太差了。面试别人,一般基于其项目经历和简历的技能栈交流。没想到,这次交流成了授课了。“大处着眼,小处落手”,一个技术人员既要具备技术视野,也要理解技术细节。比较有感触的有下面几个问题。首先,我问了一个很多人平时意识不到的问题:一个.java文本文件,是如何运行起来的?本来是想检查下对Jave程序语言的运行机制的理解。如果回答可以就可以切入到虚拟机和java程序性能调优。没想到大失所望,该同学对此几乎没有概念。接下来,我问,HashMap的内部实现,不清楚,key是否可以是null,不知道。让其写一个简单的栈Stack类,实现基本的存取功能,我把结构都写了,让他下笔,写不出来。本来是考察对基本数据结构的理解,可能的话切入内存泄露。到此,我已经基本确认该同学之前基本不写代码了。但他应聘的是中高级工程师,本着负责的态度。该问的还得继续。看项目经历吧,有个做OA系统的经验,熟悉Jbpm,看简历写着熟悉设计模式。好吧,我问:如果让你设计一个简单的流程引擎,你怎么设计,大概会用到什么设计模式。我想着,怎么着,你也应该知道责任链模式和观察者模式吧,退一步讲,一个好的系统,工厂模式、建造者模式不可能没有。对方一脸懵逼,说,没去了解过。说实在的,当时真的是有点不耐烦了。看到简历有mybatis技能,问动态sql拼装有哪些常用标签,回答差强人意。问得深一点:平时使用mybatis基本都是使用接口声明,然后注入直接调用接口方法,即可完成数据库操作。有没有疑惑过,为什么没看到实现该接口的类,就可以调用方法。要知道接口是不能实例化的。本意是考察对代理类生成(代理模式)的理解,不出所料,完全没有概念。随后简单问了下数据库,居然不知道索引是一棵树,,,随后草草收场。简单描述本次面试经历,我是觉得该面试者是一个典型。“大处着眼”,他不知道技术和框架出现的背景是什么,技术视野窄,不知道用什么技术解决实际问题,也没有关心前沿技术(分布式、服务化、大数据);“小处着手”,他没有探究内部原理的好奇心和意识,小一点说连“点进去JDK类的实现方法”的欲望都没有。我不知道这样技术深度和广度都没有的水平,怎么能实现他所说的“三年成为一个优秀的架构师”的愿望。感慨万千啊。###多线程和虚拟机。实际工作中,大部分程序员可能几乎不用,但这两项技能是你面试所谓高级工程师的敲门砖,也是你在机会到来的时候能否顶上去的弹药库。很多人,把这两部看的太高深,望而却步,我觉得一个重要原因就是大部分博客和书籍写的太差,只讲结果不谈背景。比如,讲到虚拟机,上来就以hotspot为例,内存模型,各种分区、回收算法;讲到多线程,上来就各种synchronized关键字、各种锁、线程池怎么用。新手看到就蒙了。要知道,一切技术的出现都是有背景的。所有技术的出现都是基于计算机原理和体系结构的。为了解决特定问题,人们基于计算机理解的语言才创造了各种解决问题的方法,也就是说这些解决方案不过是践行某种思想的一种体现罢了。先说虚拟机,我们都知道Java程序运行在虚拟机上,虚拟机又和操作系统打交道,最终通过二进制指令操纵电子电路运行。完成数据的读取,存储,运算和输出。虚拟机在加载.class文件的时候,会在内存开辟一块区域“方法区”,专门用来存储类的基本信息,同时在“堆”区为这些类生成一个Class对象,作为类的“镜像”或“模具”,为反射提供基础。程序运行过程中,对象不断的生成和死亡,有的朝生暮死(大多数对象都这样,最常见的是方法内部生成的临时对象),有的壮年而亡,有的长命百岁,有的长生不死除非世界毁灭(虚拟机关闭,典型的如servlet)。对象生要吃喝,死了得埋,所以虚拟机就不停的申请内存、回收内存。对象的生成方法很多,new、反射等,对象回收的方法也有很多,这就是GC,标记-清除、复制、标记-整理等等。垃圾回收,顾名思义,得确定垃圾是什么、在那里、如何回收。对象的生命周期不同,回收的方法不一样。假如让你设计垃圾回收,你该怎么做?大多数人都会想到,后台启动一个线程,隔一段时间(或达到某种状态,去堆用掉了80%),扫描垃圾对象,然后清除,然后继续执行原来的程序(串行收集器)。恭喜你,你也可以设计虚拟机了。但不幸的是,情况往往比你想象的复杂。效率、安全性、对原程序的影响,都是你要考虑的。人们最先发现,对象生命周期不同,用同一种GC方法,实在是效率差,怎么办?就如hotspot的方案,堆区根据对象生命周期不同,分成了Eden、Survivor0、Survivor1和Old区。每个区采用了不同的清理算法。多核的出现,自然人们会想到并行收集器,即多个回收线程一起跑;为了将对原程序影响降到最低(STW),又出现了并发收集器。这些,本质上,就是抽象分层思想的体现。类似于,重构代码中的,抽离属性和抽离方法。这种思想,我认为是计算机最重要的思想。可以讲三天三夜。如分布式服务中,根据业务模型,分拆用户服务、商品服务、订单服务。到此为止,虚拟机优化就涉及到两大方面,各个区的大小怎么划分最优、垃圾回收算法怎么选择最优。直接点,就是JVM参数调整。但关键在于,给你一个系统(可能是一个陌生的系统,我说的陌生可能就是你开放的系统,只是每个人负责的只是一个模块,对系统整体不熟悉),你怎么样能恰当估算系统业务情况,进而有针对性的收集系统数据,根据场景,确定优化的方向点,然后找到这个点对应的虚拟机参数,调整参数,或者,优化代码。注意,一切优化必须基于业务模型。不同业务系统、甚至同一套系统不同用户基数调整的方向都不一样。平时,我遇到的情况大概分为两种,一种是堆的问题,比如代码问题导致List或map越来越大,或者是string使用不当,造成频繁old gc;某个外部组件调用,生成大量代理类无法销毁。还有一种是线程栈,线程阻塞甚至死锁的问题。多线程使用不当,比不使用还坑爹。多线程,任何一个程序员都知道,但实际工作中,大部分程序员每天面对的基本是业务问题的CRUD和Bug定位,貌似没有直接接触多线程的机会。大家知道程序运行的时候,最关键的是内存和cpu,而cpu运算的时候,是要从内存取值,当然很多时候是从缓存取值的,然后放入寄存器,参与运算,得到结果,先放入寄存器,然后放入内存。程序执行的指令也放在寄存器,它记录了当前程序执行的地址。用一句话概括:程序=数据结构+算法。CPU运算需要知道,我要执行什么程序、我的程序数据怎么获取。大家应该看出问题来了吧?首先,线程执行是语言指令寄存器的,也就是当你切换线程的时候,得从虚拟机的程序计数器(PC)把该线程的执行指令放到指令寄存器,当然线程涉及的其他资源也要切换,比如IO设备。这些都是需要耗费资源的,这就是所谓的线程上下文切换。大学时候,记得很清楚的一句话:线程是CPU执行的最小单位。当时没怎么理解,后来想CPU执行程序,总得知道执行什么吧,那得准备指令寄存器的值,原材料得有吧,就可能涉及文件系统、网络资源吧,运算结果得输出到内存、文件或者网络吧。这些都是资源啊。所以,线程创建是一笔很大的开销。当然,如果你就一个线程,那就无所谓了,反正资源都是我的,想怎么用就怎么用。所以,很多时候,单线程比多线程快。很多面试宝典,有这么一道题:Java线程的start和run方法有什么区别?通过我上面关于线程执行的分析,应该一目了然。我用一个做饭的例子说明,start需要你买菜、准备锅碗瓢盆油盐酱醋、洗菜切肉,而run则是往锅里放油放菜炒。大家可以看到,Thread源码的start0是个native方法,也就是资源准备是虚拟机帮你做了。你不用管我菜是怎么买的、价钱多少。当然了,如果菜市场很远,一直没买到,或者排队很长,甚至被别人插队,那你这顿饭就一直做不上。这就是所谓的线程阻塞了。如果两个厨师都在做饭,一个拿着酱油想要醋,一个拿着醋想要酱油,互不相让,就出现所谓的死锁。不好意思,扯远了。关于start和run,如果把方法名改为:applyResourceAndPerformAction和doConcreteActions,是不是很容易理解?很多人面试的时候,背一下宝典,原理根本不清楚。你能指望他处理复杂问题?线程必须的资源虚拟机帮你做了,你需要的就是告诉线程你具体做什么,所以实现线程的几种方式就有了,1、继承Thread目的重写run方法;2、实现Runnable接口,实现run方法;3实现Callable接口,回调获取线程结果。1使用了继承,2和3使用了组合,内部持有了你所实现的类,更加灵活。你看,多用组合少用继承的原则就这么体现了。第二点,上面说到了,一个数值,进入CPU运算,经过了内存、多级缓存、寄存器,也就是说,当多线程运算同一个值的时候,是需要把值从主内存拿到该线程工作内存(寄存器)中的,当一个线程计算完毕(CPU首先把运算结果放到寄存器),还没刷新到主内存的时候,另一个线程从主内存取到的是旧值。JVM运行的每个线程都有自己的线程栈,不同线程运行的时候,都要复制主内存的一份副本到工作内存。怎么保证每个线程拿到的数据是最新的,这就是同步机制。volatile和synchronized,就是为了解决这个问题的。首先,谁都能想到的最直接的办法就是:共享变量同一时刻只允许有一个线程操作。这样就保证了所有线程要么拿不到值,要么拿到的值是“纯粹”的。于是有了synchronized,用来告诉虚拟机:这个地方是圣地,不允许多个人同时涉足。这里有一把锁,必须拿到锁才能进入,其他人要想进来必须等待。Java中的锁,可以是this对象、方法、类,也可以是声明的某个变量。锁的范围,可以是小块代码段,可以是整个方法区,甚至是所有方法。一定要注意锁和锁的范围,这是两个维度的事情。虚拟机会在锁对象和线程之间建立联系,其他线程跑到锁对象的时候,会看到:哦,其他哥们已经来了,我先等着吧。特别注意,不要以为对象和类的定义一样,不过是属性和方法的集合,类和对象是两回事。类似模具和产品的关系。虚拟机生成一个对象,这个对象有很多额外信息,起码有对象内存地址你是知道的吧?所以,要标识这个对象当前被哪个线程占有,是一件很容易的事情。感兴趣的同学,可以去看看对象在内存中的布局。我们很快发现,上面的方法有点粗暴,也不够灵活。很多时候,我们不关心共享值在被谁操作,我只关当前这个值“到底”是什么。所以,就有了volatile,大部分博客提到volatile,就一句话:保证可见性,不保证原子性。这什么鬼?实际上,如果一个共享变量声明为volatile,等于告诉虚拟机控制的所有线程:这个变量有点帅,要请他出山必须亲自去他老家——主内存去请,回来的时候也要尽快送回老家。所以,CPU计算的时候要从主内存取值,计算完毕,直接就写入主内存,不会写到高速缓存了。这就是所谓的“可见性”,也就是当前这个值是什么,你是完全知道的。至于不保证原子性,就很明显了,这个值谁都可以取来运算,从计算机角度来讲,跟普通变量的区别就在于:效率差了。因为写入和读取高速缓存,效率远远高于内存。一路题外话,不要以为数据库插入数据就直接到磁盘了,其实写入的也是缓存,由后台线程刷到磁盘的。这样既可以起到缓冲的作用,又可以提高效率。不然你以为怎么能那么快。其实,从底层到高层,从硬件到软件,很多原理都是相通的。————————————感谢朋友们的认可和指正。本文是有感而发,因为看过了太多坑人的博客和书籍,感慨自己走过的弯路,不希望其他初学者被网上互相抄袭的博客和东拼西凑的书籍浪费时间,想以一个相对宏观的视野来描述一个概念,力求通俗易懂,所以没有深入太多细节,简化了很多模型,给部分朋友造成了疑惑,说声抱歉。也没有配图,都是抽时间手机码字,打个分割线都费劲,图呢,其实网上都有。记得我在另外一篇答案中提到,计算机程序(不仅仅各种语言的代码,一切能向计算机发出指令的序列都是程序,当然包括Java虚拟机)的努力方向:最大化利用计算机资源。多线程就是如此,一个CPU密集型的任务在跑,你让IO干等着,这不是浪费吗?所以,这时候你启动一个IO密集型的任务,资源利用率就提升了。当然,这是一种简化模型,实际上一个人任务的不同阶段,需要的计算机资源是不同的,如果你能合理安排多个任务的执行逻辑,资源利用率就会很大提升。我们学习程序语言,一定不要被束缚到语言细节和规范上去,而要从计算机逻辑执行层面思考问题。因为细节和规范都是人为设定的,是大牛抽象计算机逻辑后的加工品,你囿于此,其实是在理解别人的思想,而不是理解计算机。我们常说的高层依赖于抽象而不依赖于底层,是一样的意思。说了这么多,想表达的就是,对技术问题,要有思考的深度,要寻根溯源,要高屋建瓴。回到多线程。上面提到synchronized,必须多说几句,这对理解锁的本质至关重要。多线程和锁,首先请大家记住一个场景:多人上厕所。多线程和锁,一个是线程,一个是对象。一个在私有的线程栈中,一个在共享的堆中。如何标识某个线程持有某个锁对象?如何如何标志某个对象被某个线程锁定?很显然,线程栈中开启一片区域“栈帧”存储对象锁记录,堆中对象有对象头(对象头主要保存了对象的类元数据,以及对象的运行时状态,其中就包括了锁线程和GC分代等信息。)可以标识被哪个线程锁定。实际上,虚拟机就是利用对象头和monitor(后面讲)来实现锁的。回到多人上厕所,人比做线程,厕所比做共享对象,锁比做对象头,monitor比做钥匙。synchronized锁的是一个对象,或者是类的某个实例,或者是类本身(即常量池的Class)。
synchronized内部原理是通过对象内部的一个叫做监视器(monitor)来实现的。本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换需要从用户态转换到核心态,这个成本非常高,这就是为什么synchronized效率低的原因。比如Hashtable(再次吐槽小写t,浑身难受)和用Collections.synchronizedMap装饰的HashMap,内部都使用了 synchronized,所以性能差,不是因为“它性能差”,而是因为“它使用的同步方式”性能差,那天人家底层重写了性能高了你怎么办?很多时候,点下鼠标进入源码看几眼就知道的东西,没必要死记硬背。synchronized这种依赖于操作系统所实现的锁我们称之为“重量级锁”。JDK中对Synchronized做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。别被这些名词砸晕了,这些锁的名字很有误导性,其实是对获取锁的方式的优化,不是锁。所谓锁的优化,主要方向是优化获取锁的方式和加锁(释放)的方式。我不想一一解释枯燥名词。还是用上厕所举例。重量级锁可以认为是,你去上厕所,得先去管理处(人或者机器)登记并拿到钥匙上厕所,这个过程可以认为存在一次“用户态”到“内核态”的切换。是非常重量级的。这里我必须强调一下,你的目标是上厕所,不是加锁,加锁只是为了你更好的上厕所。线程也一样,目的是为了完成某项任务。加锁是不得以为之的。假如一层楼就你一个人,一个厕所, 你觉得还有必要去登记吗?要什么自行车?直接上啊。这就是无锁状态;如果这层楼还有一个哥们,但他尿泡比较强悍,一天不上厕所。厕所门上有个显示器,能显示上次上厕所的是谁、期间有没有其他人上厕所,那你上的时候,只要看下显示器就知道:没别人上过,还是我,照片都没变,不用刷脸,此厕可直接上。这就是偏向锁,因为“偏向你”;假如这个哥们偶尔也上一次,这次你发现厕所有别人上过,因为显示器上有他照片,那你就得重新刷脸,好吧,那我再刷了上吧,大部分时候,里面都没这哥们,你可顺利上厕所,这叫轻量级锁;如果某天这哥们腹泻(我一同事吃湖南蒸菜有过一次),那你悲剧了,你每次上的时候,不仅显示器不是你,你想刷脸进入,发展里面还有人。没办法,只能去管理处登记等待了,变成了重量级锁。锁升级是不会降级的。这里,重量级锁涉及操作系统的处理,而偏向锁和轻量级锁涉及CAS,硬件可以搞定,效率更高。上述锁状态转移和加锁(解锁不讲了)是由虚拟机(配合操作系统)完成的,我们不可见,既然是虚拟机控制,当然就有相关参数,如是否启用偏向锁,我忘了参数名字,但我知道肯定有这样的参数。如果面试我的面试官因为我不知道参数名字鄙视我,我能反怼死他。记个别人定的名字很自豪?上面讲到重量级锁的时候,其实就是锁竞争很激烈的时候。比如早上高峰期,厕所坑位紧缺,排队的人很多,如果你一直等,等待的状态就叫“自旋”,当然你可以自旋十分钟左右后离开(虚拟机自旋也有参数控制),因为你觉得里面的哥们玩手机不知道啥时候结束,你有更重要的事情要干,还不如去外面登记等通知。显然,自旋的前提是你知道上一个哥们不会很久。多次之后,你会摸清这些人上厕所的时间后,你自旋起来就更有针对性了,这叫“适应性自旋”。还有,锁消除,锁粗化,比如基本没人用的StringBuffer、Vector,你用在某个方法中,其实根本没必要加锁,或者说比如连续的append,没必要每次都加锁,虚拟机就会进行锁消除或者锁粗化处理。上面讲了这么多,主体是线程和锁对象,核心是获取锁的方式和锁定的方式,还有,不加锁或者“伪加锁”是不是能搞定?再次强调一遍,线程生来是为了完成任务的,不是为了和锁纠缠的。多线程竞争锁的时候,肯定涉及到线程的排队,新来的线程怎么处理,是去竞争锁还是直接排队?排队中的线程,那些有资格竞争锁?有资格的线程,那个拿到锁(只是拿到锁,还未执行共享区)?不管怎么实现,这些东西是必须要考虑的。你在synchronized没见到,是因为虚拟机帮你处理了,涉及的队列也是虚拟机在维护。重量级锁的时候,又涉及和操作系统信号的交互。当然,要是你不用和操作系统进行如Mutex Lock这样“重量级的”交互也能更好、更快、更好的处理同步,那你就是大牛了。大牛当然是存在,比如李老头。下面会开始讲更加灵活的、细粒度、可定制的Lock锁。可以认为是把synchronized加锁的过程、锁定的方式等流程中细节拆分出来,用灵巧的实现方式实现线程同步。再后面会讲对象的wait、notify,线程的sleep,主体不一样,思考的角度不一样。今天先到这里——手机码字,持续更新,,,——————百姓知道什么鬼,说是保存为草稿,切了个微信,回来没了,在草稿里面也没有,玩我呢?刚写的关键的几段没了!有时间再写吧。
面试造火箭,入职拧螺丝
百姓网 Logo
百姓网 熊掌号
简单,快速,搞得定!
是哒是哒,蓝河姆爱普的奶粉确实不错。我家娃喝了两个月了,排便正常也不上火,而且之前总是动不动就感冒,现在也不了,体格...
前几天闺蜜给我闺女带了两罐蓝河姆爱普奶粉,小闺女挺喜欢喝的。
好像不少明星都在东田做过造型,威娜不错的~
我在投你我贷的“月月升”,流程很简单的,满30天后就可以随时地退出,本息是有保障的。而且投资时间越长,收益越高;不光如此...
术前夫妻双方可以和医生进行沟通,详细了解手术的流程,为手术中可能出现的问题做好准备。此外,术前应保持良好的心态,避免...
如果男女双方都无疾病,正常情况下每个月的怀孕可能性也只有10%。如果男女双方有疾病,那成功率只有1%左右。现在通过试管婴儿...
对于月经少的情况,要尽量避免熬夜,保证睡眠。饮食要清单,少辛辣油腻,咖啡、浓茶等饮品要少喝。养成良好的作息饮食习惯。...
首先恭喜您要做妈妈了,至于新生儿喝什么牌子的奶粉好,这里作为一个母亲推荐施恩,我家宝宝一直以来都是喝的,感觉我家宝宝...
&2018 百姓网股份有限公司 客服电话:400-036-3650 或 021-(周一至周日 9:00~18:00)
沪ICP备号 沪ICP证B2- 沪公网安备16号

我要回帖

更多关于 手机内存不足如何处理 的文章

 

随机推荐