
JAVA中几种常见死锁及对策:解决死锁没有简单的方法,这是因为线程产生死锁都各有各的原因,而且往往具有很高的负载。大多数软件测试产生不了足够多的负载,所以不可能暴露所有的线程错误。在这里中,下面将讨论开发过程常见的4类典型的死锁和解决对策。(1)数据库死锁在数据库中,如果一个连接占用了另一个连接所需的数据库锁,则它可以阻塞另一个连接。如果两个或两个以上的连接相互阻塞,则它们都不能继续执行,这种情况称为数据库死锁。数据库死锁问题不易处理,通常数据行进行更新时,需要锁定该数据行,执行更新,然后在提交或回滚封闭事务时释放锁。由于数据库平台、配置的隔离级以及查询提示的不同,获取的锁可能是细粒度或粗粒度的,它会阻塞(或不阻塞)其他对同一数据行、表或数据库的查询。基于数据库模式,读写 *** 作会要求遍历或更新多个索引、验证约束、执行触发器等。每个要求都会引入锁。此外,其他应用程序还可能正在访问同一数据库模式中的某些对象,并获取不同应用程序所具有的锁。所有这些因素综合在一起,数据库死锁几乎不可能被消除了。值得庆幸的是,数据库死锁通常是可恢复的:当数据库发现死锁时,它会强制销毁一个连接(通常是使用最少的连接),并回滚其事务。这将释放所有与已经结束的事务相关联的锁,至少允许其他连接中有一个可以获取它们正在被阻塞的锁。由于数据库具有这种典型的死锁处理行为,所以当出现数据库死锁问题时,数据库常常只能重试整个事务。当数据库连接被销毁时,会抛出可被应用程序捕获的异常,并标识为数据库死锁。如果允许死锁异常传播到初始化该事务的代码层之外,则该代码层可以启动一个新事务并重做先前所有工作。当出现问题就重试,由于数据库可以自由地获取锁,所以几乎不可能保证两个或两个以上的线程不发生数据库死锁。此方法至少能保证在出现某些数据库死锁情况时,应用程序能正常运行。(2)资源池耗尽死锁客户端的增加导致资源池耗尽死锁是由于负载而造成的,即资源池太小,而每个线程需要的资源超过了池中的可用资源。假设连接池最多有10个连接,同时有10个对外部并发调用。这些线程中每一个都需要一个数据库连接用来清空池。现在,每个线程都执行嵌套的调用。则所有线程都不能继续,但又都不放弃自己的第一个数据库连接。这样,10个线程都将被死锁。研究此类死锁,会发现线程存储中有大量等待获取资源的线程,以及同等数量的空闲且未阻塞的活动数据库连接。当应用程序死锁时,如果可以在运行时检测连接池,就能确认连接池实际上已空。修复此类死锁的方法包括:增加连接池的大小或者重构代码,以便单个线程不需要同时使用很多数据库连接。或者可以设置内部调用使用不同的连接池,即使外部调用的连接池为空,内部调用也能使用自己的连接池继续。(3)单线程、多冲突数据库连接死锁对同一线程执行嵌套的调用有时出现死锁,此情形即使在非高负载系统中通常也会发生。当第一个(外部)连接已获取第二个(内部)连接所需要的数据库锁,则第二个连接将永久阻塞第一个连接,并等待第一个连接被提交或回滚,这就出现了死锁情形。因为数据库没有注意到两个连接之间的关系,所以数据库不会将此情形检测为死锁。这样即使不存在并发,此代码也将导致死锁。此情形有多种具体的变种,可以涉及多个线程和两个以上的数据库连接。(4)Java虚拟机锁与数据库锁冲突这种情形发生在数据库锁与Java虚拟机锁并存的时候。在这种情况下,一个线程占有一个数据库锁并尝试获取Java虚拟机锁。同时,另一个线程占有Java虚拟机锁并尝试获取数据库锁。此时,数据库发现一个连接阻塞了另一个连接,但由于无法阻止连接继续,所以不会检测到死锁。Java虚拟机发现同步的锁中有一个线程,并有另一个尝试进入的线程,所以即使Java虚拟机能检测到死锁并对它们进行处理,它还是不会检测到这种情况。 总而言之,JAVA应用程序中的死锁是一个大问题——它能导致整个应用程序慢慢终止,还很难被分离和修复,尤其是当开发人员不熟悉如何分析死锁环境的时候。五死锁的经验法则笔者在开发中总结以下死锁问题的经验。(1)对大多数的Java程序员来说最简单的防止死锁的方法是对竞争的资源引入序号,如果一个线程需要几个资源,那么它必须先得到小序号的资源,再申请大序号的资源。可以在Java代码中增加同步关键字的使用,这样可以减少死锁,但这样做也会影响性能。如果负载过重,数据库内部也有可能发生死锁。(2)了解数据库锁的发生行为。假定任何数据库访问都有可能陷入数据库死锁状况,但是都能正确进行重试。例如了解如何从应用服务器获取完整的线程转储以及从数据库获取数据库连接列表(包括互相阻塞的连接),知道每个数据库连接与哪个Java线程相关联。了解Java线程和数据库连接之间映射的最简单方法是向连接池访问模式添加日志记录功能。(3)当进行嵌套的调用时,了解哪些调用使用了与其它调用同样的数据库连接。即使嵌套调用运行在同一个全局事务中,它仍将使用不同的数据库连接,而不会导致嵌套死锁。(4)确保在峰值并发时有足够大的资源池。(5)避免执行数据库调用或在占有Java虚拟机锁时,执行其他与Java虚拟机无关的 *** 作。 最重要的是,多线程设计虽然是困难的,但在开始编程之前详细设计系统能够帮助你避免难以发现死锁的问题。死锁在语言层面上不能解决,就需要一个良好设计来避免死锁。
一般需从三点入手。
一、程序本身支持高并发。
简单来说就是要优化我们的代码。
1、避免使用错误的方式,尽量不用instanceof做条件判断,不要将数组声明为:public static final 。
2、使用java中效率高的类,比如尽量使用HashMap 和ArrayList ,除非必要,否则不推荐使用HashTable和Vector ,后者由于使用同步机制,而导致了性能的开销。
3、尽量指定类的final修饰符 带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如javalangString。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50% 。
4、尽量重用对象,避免频繁的使用new对象。对于整个应用只需要存在一个实例的类,我们可以使用单例模式。对于工具类可以使用静态方法的方式访问。
用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。
在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实
例非常简单。例如,
Java代码 收藏代码下面是Factory模式的一个典型实现:
public static Credit getNewCredit() {
return new Credit();
}
改进后的代码使用clone()方法,如下所示:
private static Credit BaseCredit = new Credit();
public static Credit getNewCredit() {
return (Credit) BaseCreditclone();
}
上面的思路对于数组处理同样很有用。
5、特别是String 对象的使用中,出现字符串连接情况时应用StringBuffer 代替。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。
6、StringBuffer 的使用:StringBuffer表示了可变的、可写的字符串。
它有三个构造方法 :
StringBuffer (); //默认分配16个字符的空间StringBuffer (int size); //分配size个字符的空间
StringBuffer (String str); //分配16个字符+strlength()个字符空间
你可以通过StringBuffer的构造函数来设定它的初始化容量,这样可以明显地提升性能。这里提到的构造函数是StringBuffer(int length),length参数表示当前的StringBuffer能保持的字符数量。你也可以使用ensureCapacity(int minimumcapacity)方法在StringBuffer对象创建之后设置它的容量。首先我们看看StringBuffer的缺省行为,然 后再找出一条更好的提升性能的途径。
StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候,因为没有设置初始化字符长度,StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容量 的时候,它会将自身容量增加到当前的2倍再加2,也就是(2旧值+2)。如果你使用缺省值,初始化之后接着往里面追 加字符,在你追加到第16个字符的时候它会将容量增加到34(216+2),当追加到34个字符的时候就会将容量增加到 70(234+2)。无论何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和 新字符都拷贝一遍――这也太昂贵了点。所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来 立竿见影的性能增益。
StringBuffer初始化过程的调整的作用由此可见一斑。所以,使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。
7、尽量使用局部变量,调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。
8、不要重复初始化变量 默认情况下,调用类的构造函数时, Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成00,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。
9、在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。
10、Java 编程过程中,进行数据库连接、I/O流 *** 作时务必小心,在使用完毕后,即使关闭以释放资源。因为对这些大对象的 *** 作会造成系统大的开销,稍有不慎,会导致严重的后果。
11、由于JVM的有其自身的GC机制,不需要程序开发者的过多考虑,从一定程度上减轻了开发者负担,但同时也遗漏了隐患,过分的创建对象会消耗系统的大量内存,严重时会导致内存泄露,因此,保证过期对象的及时回收具有重要意义。JVM回收垃圾的条件是:对象不在被引用;然而,JVM的GC并非十分的机智,即使对象满足了垃圾回收的条件也不一定会被立即回收。所以,建议我们在对象使用完毕,应手动置成null。
12、在使用同步机制时,应尽量使用方法同步代替代码块同步。
13、尽量减少对变量的重复计算
例如:for(int i = 0;i < listsize; i ++) {
…
}
应替换为:
for(int i = 0,int len = listsize();i < len; i ++) {
…
}
14、尽量采用lazy loading 的策略,即在需要的时候才开始创建。
例如:String str = “aaa”;
if(i == 1) {
listadd(str);
}
应替换为:
if(i == 1) {
String str = “aaa”;
listadd(str);
}
15、慎用异常
异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。 异常只能用于错误处理,不应该用来控制程序流程。
16、不要在循环中使用trycatch,应把其放置在最外层。
17、合理的使用Java类 javautilVector。
简单地说,一个Vector就是一个javalangObject实例的数组。Vector与数组相似,它的元素可以通过整数形式的索引访问。但是,Vector类型的对象在创建之后,对象的大小能够根据元素的增加或者删除而扩展、缩小。请考虑下面这个向Vector加入元素的例子:
Java代码 收藏代码Object obj = new Object();
Vector v = new Vector(100000);
for(int I=0;
I<100000; I++) { vadd(0,obj); }
并发量和很多都有关系,比如代码质量,硬件资源,数据库等。
一般提升并发量可以从下面几个方面入手
多应用服务器-集群部署
F5、负载均衡
缓存/数据库:读写分离,切片
等
1简单性
Java看起来设计得很像C,但是为了使语言小和容易熟悉,设计者们把C语言中许多可用的特征去掉了,这些特征是一般程序员很少使用的。例如,Java不支持goto语句,代之以提供break和continue语句以及异常处理。Java还剔除了C的 *** 作符过载(overload)和多继承特征,并且不使用主文件,免去了预处理程序。因为Java没有结构,数组和串都是对象,所以不需要指针。Java能够自动处理对象的引用和间接引用,实现自动的无用单元收集,使用户不必为存储管理问题烦恼,能更多的时间和精力花在研发上。
2面向对象
Java是一个面向对象的语言。对程序员来说,这意味着要注意应中的数据和 *** 纵数据的方法(method),而不是严格地用过程来思考。在一个面向对象的系统中,类(class)是数据和 *** 作数据的方法的集合。数据和方法一起描述对象(object)的状态和行为。每一对象是其状态和行为的封装。类是按一定体系和层次安排的,使得子类可以从超类继承行为。在这个类层次体系中有一个根类,它是具有一般行为的类。Java程序是用类来组织的。
Java还包括一个类的扩展集合,分别组成各种程序包(Package),用户可以在自己的程序中使用。例如,Java提供产生图形用户接口部件的类(javaawt包),这里awt是抽象窗口工具集(abstractwindowingtoolkit)的缩写,处理输入输出的类(javaio包)和支持网络功能的类(javanet包)。
3分布性
Java设计成支持在网络上应用,它是分布式语言。Java既支持各种层次的网络连接,又以Socket类支持可靠的流(stream)网络连接,所以用户可以产生分布式的客户机和服务器。
网络变成软件应用的分布运载工具。Java程序只要编写一次,就可到处运行。
4编译和解释性
Java编译程序生成字节码(byte-code),而不是通常的机器码。Java字节码提供对体系结构中性的目标文件格式,代码设计成可有效地传送程序到多个平台。Java程序可以在任何实现了Java解释程序和运行系统(run-timesystem)的系统上运行。
在一个解释性的环境中,程序开发的标准“链接”阶段大大消失了。如果说Java还有一个链接阶段,它只是把新类装进环境的过程,它是增量式的、轻量级的过程。因此,Java支持快速原型和容易试验,它将导致快速程序开发。这是一个与传统的、耗时的“编译、链接和测试”形成鲜明对比的精巧的开发过程。
5稳健性
Java原来是用作编写消费类家用电子产品软件的语言,所以它是被设计成写高可靠和稳健软件的。Java消除了某些编程错误,使得用它写可靠软件相当容易。
Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。
可靠性方面最重要的增强之一是Java的存储模型。Java不支持指针,它消除重写存储和讹误数据的可能性。类似地,Java自动的“无用单元收集”预防存储漏泄和其它有关动态存储分配和解除分配的有害错误。Java解释程序也执行许多运行时的检查,诸如验证所有数组和串访问是否在界限之内。
异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。
6安全性
Java的存储分配模型是它防御恶意代码的主要方法之一。Java没有指针,所以程序员不能得到隐蔽起来的内幕和伪造指针去指向存储器。更重要的是,Java编译程序不处理存储安排决策,所以程序员不能通过查看声明去猜测类的实际存储安排。编译的Java代码中的存储引用在运行时由Java解释程序决定实际存储地址。
Java运行系统使用字节码验证过程来保证装载到网络上的代码不违背任何Java语言限制。这个安全机制部分包括类如何从网上装载。例如,装载的类是放在分开的名字空间而不是局部类,预防恶意的小应用程序用它自己的版本来代替标准Java类。
7可移植性
Java使得语言声明不依赖于实现的方面。例如,Java显式说明每个基本数据类型的大小和它的运算行为(这些数据类型由Java语法描述)。
Java环境本身对新的硬件平台和 *** 作系统是可移植的。Java编译程序也用Java编写,而Java运行系统用ANSIC语言编写。
8高性能
Java是一种先编译后解释的语言,所以它不如全编译性语言快。但是有些情况下性能是很要紧的,为了支持这些情况,Java设计者制作了“及时”编译程序,它能在运行时把Java字节码翻译成特定CPU(中央处理器)的机器代码,也就是实现全编译了。
Java字节码格式设计时考虑到这些“及时”编译程序的需要,所以生成机器代码的过程相当简单,它能产生相当好的代码。
9多线索性
Java是多线索语言,它提供支持多线索的执行(也称为轻便过程),能处理不同任务,使具有线索的程序设计很容易。Java的lang包提供一个Thread类,它支持开始线索、运行线索、停止线索和检查线索状态的方法。
Java的线索支持也包括一组同步原语。这些原语是基于监督程序和条件变量风范,由CARHaore开发的广泛使用的同步化方案。用关键词,程序员可以说明某些方法在一个类中不能并发地运行。这些方法在监督程序控制之下,确保变量维持在一个一致的状态。
10动态性
Java语言设计成适应于变化的环境,它是一个动态的语言。例如,Java中的类是根据需要载入的,甚至有些是通过网络获取的。
1、在远程server里搭建一个>
在Java程序界流行着一种默认的说法叫“黄金5年”,意思是说,一个Java程序员从入职的时候算起,前五年的选择直接影响着整个职业生涯的发展方向和薪资走向。
而这5年,也决定了一个程序员能否成为职业大牛的可能。那么,在这黄金5年,Java程序员如何做才能实现自我突破呢?1)对初学者建议
首先,对尚未进入职场工作的在校生以及准备转行Java的同学的建议:
一定要从最基础的知识学起,学习Java基础的时候,应该尽量多动手写,写出来运行一下,会发现很多问题。
学完基础内容之后,对Java有一个基本的了解之后,不要着急着进入更高级的学习,尝试用Java语言写出一些简单的程序锻炼自己。
为此,动力节点开展了线上直播班型「Java夜校班」,助你从0基础到就业。
1因为从零基础到就业,只用6个月6个月,就能把小白变成Java程序员?夜校班可以做到。
夜校班时长6个月,分为12个阶段,针对零基础学习人群,课程紧凑饱满,全程直播,讲解细腻,不同水平人的学习需求均可满足。
2因为是线上直播+课后作业首先,在选择Java在线培训班时,第一个应该注意的就是培训机构是录播还是直播?
Java夜校班与其他机构的录播视频不同,是由总监级技术大咖直播授课,只有学员和老师随时沟通互动,学习效果才能得到最大保障。
3因为有7大实战项目夜校班含有7大真实项目实训,与企业无缝对接,让你轻松胜任开发工作。(项目内容详见后文)
4因为有全新全面的课程体系Java夜校课程大纲由动力节点线上教学总监,资深架构师亲自设计并带队授课,来自一线的技术体系,来自一线的业务需求。
2)参加工作1-2年之间
在这段时间里,想必对Java已经有了一个较为深入的了解。
首先,要非常熟悉设计模式,日常要多看一些关于代码编写优化的书,提高自己的代码能力,能够写出一手优雅的代码;
其次,在工作过程中肯定接触过并发,应该更加深入地去了解并发的相关知识;
最后,要对所使用的框架和Java的类库有一定的研究,还需要去看一些JDK中的类的源码,包括自己日常所使用的框架的源码。
总而言之,这个阶段,需要做的是深入了解Java底层和Java类库,也就是JVM和JDK的相关内容。
3)参加工作3-4年之间
处于这个阶段,说实话很难再提升代码能力了,因为这个阶段的学习会比较多样化。
在前三年的工作中,相信或多或少都会接触一些其它的技术,比如大数据、分布式缓存、分布式消息服务、分布式计算、软负载均衡等技术,你能精通任何一项,都将是你未来面试时巨大的优势。
因此,如果自己对某一项技术感兴趣,这时可以深入去研究,即使这项技术不一定会在工作时用到,但一定是相关的。
4)参加工作4-5年之间
到了这个阶段,应该遇到了所谓的“技术瓶颈期”了。不要急于提高自己的技术,此时更重要的是提高你的影响力。
可以尝试去一些知名公司提高你的就业背景;也可以发表一些技术文章去影响更多的人;还可以去Github创建一个属于自己的开源项目,打造属于自己的产品。
这个时候提高自己的影响力主要是可以给自己创造更多的机会,所以,这个阶段最大的任务是提高自己的影响力,为未来工作生涯做准备。
对于软件开发同学而言,首先要客观认识自身的情况,想清楚自己现在要什么、未来要什么,做好职业规划,从而选择不同类型的企业。
下面来再谈谈Java程序员职业发展规划和方向。
1)走向架构师程序员与架构师之间的主要区别是什么呢?
架构师,是Java开发人员非常向往的一个角色。架构师需要拥有完善和扎实的技术体系,具备较好系统分析和设计能力,丰富的项目研发经验。
架构师是一个漫长的积累过程,大部分架构师的年龄在35岁左右,这个阶段就是程序员的黄金期。
程序员要向一名架构师发展,需要进一步加强技能的修养。对于互联网公司来言,最重要的技能是对网络和分布式系统的理解,比较麻烦的是分布式系统,除了分布式,在现在大型互联网企业中,还需掌握高性能、深入浅出、性能调优、Spring、JVM等技术。
为此,动力节点为大家带来了架构师课程,助大家成为年薪百万的架构师。
2)走向项目经理
当然,也有一些运气比较好的程序员,不但负责项目的设计工作,而且还负责项目的管理工作,对项目经理这个工作有一定的尝试。
因此,这类程序员可以审视一下自己,是否合适这项工作,如果自己对这种工作比较满意,而且具备项目经理的要求,则可以向项目经理这个方向发展。
项目经理的工作重点已经从编程转移到对人、对技术、对进度、对项目的管理,因此,项目经理必须要了解软件开发的各个环节、了解开发的各种技术和运用、了解开发队伍人员的水平和特点。
在日常的工作和生活中就得侧重锻炼这方面的能力,还要学习一些项目管理的知识,例如:PMP、CMMI、敏捷开发管理等。
从程序员到项目经理可以使项目经理更好地理解程序员在项目中的地位和作用,了解软件开发的各种规律性的东西,从而保证项目的正常完成。
而且,项目经理的收入和地位在公司里也是比较高的,因此,走向项目经理是程序员的另一个发展空间。
3)走向管理层
很多程序员希望自己能成为公司中的主管、或项目经理等管理人员,无论是收入还是地位都会相对高一些。
另外,还有一些程序员可能厌倦了日复一日枯燥而繁琐的编程工作,希望借助走向管理岗摆脱这种步步紧逼的工作状态。
当然,走向管理者就要求程序员要比一般人做更多的准备,做更多的转型工作。
由于软件公司的管理者不同于一般公司的管理者,专业能力越强,管理起来就越得心应手,没有专业能力的管理,遇到的问题很多,也很难解决。
所以,想要往管理者发展的程序员,一定要把编程、项目设计、项目管理等基础打好,这样转型到管理者成功率就会高一些,而且到时候也能更加得心应手。
分主机跑,有两种情况,一种是分布式系统,前端,服务端,数据库分别跑在不同的主机上,分担了压力。一种是集群,每一台上跑的都是一样的程序,一样的配置。同样的访问请求被分发到了不同的机器上响应,这才是负载均衡所追求的。
分布式和集群可以独立使用,也可以混合使用。
以上就是关于JAVA程序设计,多线程且避免死锁全部的内容,包括:JAVA程序设计,多线程且避免死锁、java程序员面试时被问到:如何在j2ee项目中处理高并发量访问 该怎么回答 请仔细看题干再回答、java项目的并发量多高,如何应对的做项目过程中遇到的挑战和对策等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)