《2023编程题库:Java面试题集及参考答案.pdf》由会员分享,可在线阅读,更多相关《2023编程题库:Java面试题集及参考答案.pdf(32页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、2023编程精选题库:Java面试题集及参考答案相关概念面向对象的三个特征封装,继承,多态.这个应该是人人皆知.有时候也会加上抽象.多态的好处允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用).主要有以下优点:1.可替换性:多态对已存在代码具有可替换性.2.可扩充性:增加新的子类不影响已经存在的类结构.3.接口性:多态是超类通过方法签名,向子类提供一个公共接口,由子类来完善或者重写它来实现的.4.灵活性.5.简化性.代码中如何实现多态实现多态主要有以下三种方式:1.接口实现2.继承父类重写方法3.同一类中进行方法重载虚拟机是如何实
2、现多态的动态绑定技术(dynamic binding),执行期间判断所引用对象的实际类型,根据实际类型调用对应的方法.接口的意义接口的意义用三个词就可以概括:规范,扩展,回调.抽象类的意义抽象类的意义可以用三句话来概括:1.为其他子类提供一个公共的类型2.封装子类中重复定义的内容3.定义抽象方法,子类虽然有不同的实现,但是定义时一致的接口和抽象类的区别比较抽象类接口默认方法抽象类可以有默认的方法实现java 8之前,接口中不存在方法的实现.实现方式子类使用extends关键字来继承抽象类.如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现.子类使用implements来实现接口,需要提供
3、接口中所有声明的实现.构造器抽象类中可以有构造器,接口中不能和正常类区别抽象类不能被实例化接口则是完全不同的类型访问修饰符抽象方法可以有public,protected和default等修饰接口默认是public,不能使用其他修饰符多继承一个子类只能存在一个父类一个子类可以存在多个接口添加新方法想抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码如果往接口中添加新方法,则子类中需要实现该方法.父类的静态方法能否被子类重写不能.重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,我们一般称之为隐藏.什么是不可变对象不可变对象指对象一旦被创建,状态就不
4、能再改变。任何修改都会创建一个新的 对 象,如String、Integer及其它包装类。静态变量和实例变量的区别?静态变量存储在方法区,属于类所有.实例变量存储在堆当中,其引用存在当前线程栈.能否创建一个包含可变对象的不可变对象?当然可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。最常见的例子就是对象中包含一个日期对象的引用.java创建对象的几种方式1.采 用new2.通过反射3.采用 clone4.通过序列化机制前2者都需要显式地调用构造方法.造成耦合性最高的恰好是第一种,因此你发现无论什么框架,只要涉及到解
5、耦必先减少new的使用.switch中能否使用string做参数在idk 1.7之前,switch只能支持byte,short,char,int或者其对应的封装类以及Enum类型。从idk 1.7之 后switch开始支持String.switch 能否作用在 byte,long?可以用在byte上,但是不能用在long上.String sl=ab,String s2=a+b,String s3=a,String s4二 b户5=53+54请问55=52返回什么?返 回false.在编译过程中,编译器会将s2直接优化为ab”,会将其放置在常量池当中,s5则是被创建在堆区,相当于s5=newSt
6、ring(ab);你对String对象的intern。熟悉么?intern。方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池中创建,如果已经存在则直接返回.比如String sl=aa;String s2=sl.intern();System.out.print(sl=s2);返回 trueObject中有哪些公共方法?1.equals()2.clone()3.getClass()4.notify(),notifyAll(),wait()5.toStringjava当中的四种引用强引用,软引用弱引用虚引用不同的引用类型主要体现在GC:1.强 引 用:如果一个对象具有强引
7、用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出OutOfMemoryError错 误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象2.软 引 用:在使用软引用时,如果内存的空间足够,软引用就能继续被 使 用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。3.弱 引 用:具有弱引用的对象拥有的生命周期更短暂。因为当JVM进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能
8、迅速发现弱引用对象4.虚 引 用:顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。WeakReference 与 SoftReference 的区别?这点在四种引用类型中已经做了解释,这里简单说明一下即可:虽然WeakReference与SoftReference都有利于提高GC和内存的效率,但是WeakReference,一旦失去最后一个强引用,就会被GC回收,而软引用虽然不能阻止被回收,但是可以延迟到JVM内存不足的时候。为什么要有不同的引用类型不 像C语言,我们可以控制内存的申请和释放,在Java中有时候我们需要适当的控制对象被回
9、收的时机,因此就诞生了不同的引用类型,可以说不同的引用类型实则是对GC回收时机不可控的妥协.有以下几个使用场景可以充分的说明:1.利 用 软 引 用 和 弱 引 用 解 决0 0 M问 题:用 一 个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在 内 存 不 足 时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了0 0 M的问题.2.通 过 软 引 用 实 现Java对象的高速缓存:比如我们创建了一 Person的 类,如 果 每 次 需 要 查 询 一 个 人 的 信 息,哪 怕 是 几 秒 中 之 前 刚 刚 查 询 过 的,都 要 重 新 构
10、建 一 个 实 例,这 将 引 起 大 量Person对象的消耗,并且由于这些对象的生命周期相对较短,会 引 起 多 次GC影响性能。此时,通过软引用和HashMap的结合可以构建高速缓存,提供性能.java中=和 eqauls()的区别,equals()禾 口 hdShCOCle 的区别=是运算符,用于比较两个变量是否相等,而equals是Object类的方法,用于比较两个对象是否相等.默认Object类 的equals方法是比较两个对象的地址,此时和=的结果一样.换句话说:基本类型比较用=,比较的是他们的值.默认下,对象用=比较时,比较的是内存地址,如果需要比较对象内容,需要重写equal
11、方法equals。和 hashcode()的联系hashcode()是Object类的一个方法,返回一个哈希值.如果两个对象根据equal。方法比较相等,那么调用这两个对象中任意一个对象的hashCode。方法必须产生相同的哈希值.如果两个对象根据eqaul()方法比较不相等,那么产生的哈希值不一定相等(碰撞的情况下还是会相等的.)a.hashCode。有什么用?与a.equals(b)有什么关系hashCode()方法是相应对象整型的hash值。它常用于基于hash的集合类,如 Hashtable、HashMap、LinkedHashMap 等等。它与 equals。方法关系特别紧密。根据J
12、ava规范,使用equal()方法来判断两个相等的对象,必须具有相同的hashcode。将对象放入到集合中时,首先判断要放入对象的hashcode是否已经在集合中存在,不存在则直接放入集合.如果hashcode相等,然后通过equal。方法判断要放入对象与集合中的任意对象是否相等:如果equal。判断不相等,直接将该元素放入集合中,否则不放入.有没有可能两个不相等的对象有相同的hashcode有可能,两个不相等的对象可能会有相同的hashcode值,这就是为什么在hashmap中会有冲突。如果两个对象相等,必须有相同的hashcode值,反之不成立.可以在hashcode中使用随机数字吗?不行
13、,因为同一对象的hashcode值必须是相同的a=b与a.equals(b)有什么区别如果a和b都是对象,则a=b是比较两个对象的引用,只有当a和b指向的是堆中的同一个对象才会返回true,而a.equals(b)是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较。例如,String类重写equals()方法,所以可以用于两个不同对象,但是包含的字母相同的比较。3*0.1=0.3 返回值是什么false,因为有些浮点数不能完全精确的表示出来。a=a+b与a+=b有什么区别吗?+=操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则
14、不会自动进行类型转换.如:byte a=127;byte b=127;b=a+b;/error:cannot convert from int to byteb+=a;/ok(译者注:这个地方应该表述的有误,其实无论a+b的值为多少,编译器都会报 错,因 为a+b操作会将a、b提 升 为int类 型,所 以 将int类型赋值给byte就会编译出错)short sl=1;si=si+1;该段代码是否有错,有的话怎么改?有错误,short类型在进行运算时会自动提升为int类型,也就是说si+i的运算结果 是int类型.short sl=1;si+=1;该段代码是否有错,有的话怎么改?+=操作符会自
15、动对右边的表达式结果强转匹配左边的数据类型,所以没错.&和&的区别首先记住&是位操作,而&是逻辑运算符.另外需要记住逻辑运算符具有短路特性,而&不具备短路特性.以上代码将会抛出空指针异常.一个.java文件内部可以有类?(非内部类)只能有一个public公共类,但是可以有多个default修饰的类.如何正确的退出多层嵌套循环.1.使 用 标 号 和break;2.通过在外层循环中添加标识符内部类的作用内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立.在单个外围类当中,可以让多个内部类以不同的方式实现同一接口,或者继承同一个类.创建内部类对象的时刻不依赖于外部类
16、对象的创建.内部类并没有令人疑惑的is-a关系,它就像是一个独立的实体.内部类提供了更好的封装,除了该外围类,其他类都不能访问final,finalize 和 finally 的不同之处final是一个修饰符,可以修饰变量、方法和类。如果final修饰变量,意味着该变量的值在初始化后不能被改变。finalize方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会,但是什么时候调用finalize没有保证。finally是一个关键字,与try和catch 一起用于异常的处理。finally块一定会被执行,无论在try块中是否有发生异常。clone。是哪个类的方法?java.Iang.C
17、loneable是一个标示性接口,不包含任何方法,clone方法在object类中定义。并且需要知道clone()方法是一个本地方法,这意味着它是由c或C +或其他本地语言实现的。深拷贝和浅拷贝的区别是什么?浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的对象所引用的对象都复制了一遍。static都有哪些用法?几乎所有的人都知
18、道static关键字这两个基本的用法:静态变量和静态方法.也就是被static所修饰的变量/方法都属于类的静态资源,类实例所共享.除了静态变量和静态方法之外,static也用于静态块,多用于初始化操作:此外static也多用于修饰内部类,此时称之为静态内部类.最后一种用法就是静态导包,即import static.import static是 在JDK 1.5之后引入的新特性,可以用来指定导入某个类中的静态资源,并且不需要使用类名.资源名,可以直接使用资源名,比如:final有哪些用法final也是很多面试喜欢问的地方,能回答下以下三点就不错了:1.被final修饰的类不可以被继承2.被fin
19、al修饰的方法不可以被重写3.被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.4.被final修饰的方法,JVM会尝试将其内联,以提高运行效率5.被final修饰的常量,在编译阶段会存入常量池中.回答出编译器对final域要遵守的两个重排序规则更好:1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序.2.初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序.数据类型相关java中int charjong各占多少字节?类型位数字节数short216i
20、nt432long864类型位数字节数float432double864char21664位的JVM当中,int的长度是多少?Java中,int类型变量的长度是一个固定值,与平台无关,都 是32位。意思就 是 说,在32位 和64位 的Java虚拟机中,int类型的长度是相同的。int和Integer的区别Integer是int的包装类型,在拆箱和装箱中,二者自动转换.int是基本类型,直接存数值,而integer是 对 象,用一个引用指向这个对象.int和Integer谁占用的内存更多?Integer对象会占用更多的内存。Integer是一个对象,需要存储对象的元数据。但 是int是一个原始
21、类型的数据,所以占用的空间更少。String,String Buffer 和 StringBuilder 区别String是字符串常量,final修饰;StringBuffer字符串变量(线程安全);StringBuilder字符串变量(线程不安全).String 和 StringBufferString和StringBuffer主要区别是性能:String是不可变对象,每次对String类型进行操作都等同于产生了一个新的String对象,然后指向新的String对象.所以尽量不在对String进行大量的拼接操作,否则会产生很多临时对象,导 致GC开始工作,影响系统性能.StringBuffe
22、r是对对象本身操作,而不是产生新的对象,因此在有大量拼接的情况下,我们建议使用StringBuffer.但是需要注意现在JVM会 对String拼接做一定的优化:String s=f,This is only+simple+test”会被虚拟机直接优化成 Strings=This is only simple test”,此时就不存在拼接过程.StringBuffer 和 StringBuilderStringBuffer是线程安全的可变字符串,其内部实现是可变数组.StringBuilder是jdk 1.5新增的,其功能和StringBuffer类似,但是非线程安全.因此,在没有多线程问题的
23、前提下,使用StringBuilder会取得更好的性能.什么是编译器常量?使用它有什么风险?公共静态不可变(public static final)变量也就是我们所说的编译期常量,这里的public可选的。实际上这些变量在编译时会被替换掉,因为编译器知道这些变量的值,并且知道这些变量在运行时不能改变。这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端仍然在使用老的值,甚至你已经部署了一个新的jar。为了避免这种情况,当你在更新依赖JAR文件时,确保重新编译你的程序。java当中使用什么类型表示价格比较好?如果不是特别关心内存和性
24、能的话,使用BigDecimal,否则使用预定义精度的double类型。如何将byte转 为String可以使用String接收byte参数的构造器来进行转换,需要注意的点是要使用的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不同。可以将int强转为byte类型么?会产生什么问题?我们可以做强制转换,但是Java中int是32位的而byte是8位的,所以,如果强制转化int类型的高24位将会被丢弃,byte类型的范围是从-128到128关于垃圾回收你知道哪些垃圾回收算法?垃圾回收从理论上非常容易理解,具体的方法有以下几种:1.标记-清除2.标记-复制3.标记-整理4
25、.分代回收更详细的内容参见深入理解垃圾回收算法:http:/ 世 代:年轻、年老和永久。内存的分配是发生在年轻世代中的。当一个对象存活时间足够长的时候,它就会被复制到年老世代中。对于不同的世代可以使用不同的垃圾回收算法。进行世代划分的出发点是对应用中对象存活时间进行研究之后得出的统计规律。一般来说,一个应用中的大部分对象的存活时间都很短。比如局部变量的存活时间就只在方法的执行过程中。基于这一点,对于年轻世代的垃圾回收算法就可以很有针对性.调用System.gc()会发生什么?通 知GC开始工作,但 是GC真正开始的时间不确定.进程,线程相关说说进程,线程,协程之间的区别简而言之,进程是程序运行
26、和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程.进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高.线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位.同一进程中的多个线程之间可以并发执行.你了解守护线程吗?它和非守护线程有什么区别程序运行完毕jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程.守护线程最典型的例子就是GC线程什么是多线程上下文切换多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。创建两种线程的方式?他们有什么区别
27、?通过实现java.lang.Runnable或者通过扩展java.lang.Thread类.相比扩展Thread,实现Runnable接口可能更优.原因有二:1.Java不支持多继承.因此扩展Thread类就代表这个子类不能扩展其他类.而实现Runnable接口的类还可能扩展另一个类.2.类可能只要求可执行即可,因此继承整个Thread类的开销过大.Thread类中的start()fil run。方法有什么区别?start。方法被用来启动新创建的线程,而且start。内部调用了 run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没
28、有新的线程启动,start。方法才会启动新线程。怎么检测一个线程是否持有对象监视器Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法,这意味着“某条线程 指的是当前线程。Runnable 和 Callable 的区别Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的cal 1()方法是有返回值的,是一个泛型,和Future.FutureTask配合可以用来获取异步执行的结果。这其实是很有用的一个特
29、性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以方便获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务什么导致线程阻塞阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java提供了大量方法来支持阻塞,下面让我们逐一分析。方法说明sleepQsleepQ允许指定以毫秒为单位的
30、一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU时间,指定的时间一过,线程重新进入可执行状态。典型地,sleepQ被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止suspend()和resume()两个方法配套使用,suspend。使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resumeQ被调用,才能使得线程重新进入可执行状态。典型地,suspend()和resume()被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用resume。使其恢复。yield()yi
31、eld()使当前线程放弃当前已经分得的CPU时间,但不使当前线程阻塞,即线程仍处于可执行状态,随时可能再次分得CPU时间。调用yield()的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程wait()和notifyO两个方法配套使用,wait。使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的notifyO被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的notifyO被调用.wait。,notify。和 suspend。,resume。之间的区别初看起来它们与suspend。和resume()方法对没有
32、什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。上述的核心区别导致了一系列的细节上的区别。首先,前面叙述的所有方法都隶属于Thread类,但是这一对却直接隶属于Object类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的wait()方法导致线程阻塞,并且该对象上的锁被释放。而调用任意对象的notify。方法则导致从调用该对象的wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁
33、后才真正可执行)。其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在synchronized方法或块中调用,理由也很简单,只有在synchronized方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的synchronized方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现 IllegalMonitorStateException 异常。wait。和notifyO方法的上述特性决定了它们经常和synchro
34、nized关键字一起使用,将它们和操作系统进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于block和wakeup原语(这一对方法均声明为synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。关于wait()和notifyO方法最后再说明两点:第一:调用notifyO方法导致解除阻塞的线程是从因调用该对象的wait()方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编
35、程时要特别小心,避免因这种不确定性而产生问题。第二:除了 notifyO,还有一个方法notifyAIIQ也可起到类似作用,唯一的区别在于,调用notifyAIIQ方法将把因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend()方法和不指定超时期限的wait()方法的调用都可能产生死锁。遗憾的是,Java并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。以上我们对Java中实现线程阻塞的各种方法作了一番分析,我们重点分析了wait。和notifyO方法,因为
36、它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。产生死锁的条件1.互斥条件:一个资源每次只能被一个进程使用。2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。4循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。为什么 wait。方法和 notify()/notifyAII()方法要在同步块中被调用这是JDK强制的,wait。方法和notify()/notifyAII()方法在调用前都必须先获得对象的锁wait。
37、方法和notifyO/notifyAH。方法在放弃对象监视器时有什么区别wait。方法和notify()/notifyAII()方法在放弃对象监视器的时候的区别在于:wait。方法立即释放对象监视器,notify()/notifyAII()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。wait。与sleep。的区别关于这两者已经在上面进行详细的说明,这里就做个概括好了:sleep。来 自Thread类,和wait。来 自Object类.调用sleep。方法的过程中,线程不会释放对象锁。而 调 用w ait方法线程会释放对象锁 sleep。睡眠后不出让系统资源,w ait让其他线程可以占用
38、CPU sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒.而wait。需要配合notify。或 者notifyAII()使用为什么wait,nofity和nofityAII这些方法不放在Thread类当中一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait。方法就有意义了。如果wait。方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAII都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。怎么唤醒一个阻
39、塞的线程如果线程是因为调用了 wait。、sleep。或者join。方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了 10阻塞,无能为力,因为10是操作系统实现的,Java代码并没有办法直接接触到操作系统。什么是多线程的上下文切换多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。synchronized 和 ReentrantLock 的区别synchronized 是和 if、else、for、while一样的关键字,ReentrantLock 是类,这是二者的本质区别。
40、既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁(2)ReentrantLock可以获取各种锁的信息(3)ReentrantLock可以灵活地实现多路通知另外,二者的锁机制其实也是不一样的:ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word.FutureTask 是什么
41、这个其实前面有提到过,FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。当然,由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。一个线程如果出现了运行时异常怎么办?如果这个异常没有被捕获的话,这个线程就停止执行了。另外重要的一点是:如果这个线程持有某个某个对象的监视器,那么这个对象监视器会被立即释放Java当中有哪几种锁1.自旋锁:自旋锁在JDKL6之后就默认开启了。基于之前的观察,共享数据的锁定状态只会持
42、续很短的时间,为了这一小段时间而去挂起和恢复线程有点浪费,所以这里就做了一个处理,让后面请求锁的那个线程在稍等一会,但是不放弃处理器的执行时间,看看持有锁的线程能否快速释放。为了让线程等待,所以需要让线程执行一个忙循环也就是自旋操作。在jdk6之后,引入了自适应的自旋锁,也就是等待的时间不再固定了,而是由上一次在同一个锁上的自旋时间及锁的拥有者状态来决定2.偏向锁:在JDK1.之后引入的一项锁优化,目的是消除数据在无竞争情况下的同步原语。进一步提升程序的运行性能。偏向锁就是偏心的偏,意思是这个锁会偏向第一个获得他的线程,如果接下来的执行过程中,改锁没有被其他线程获取,则持有偏向锁的线程将永远不
43、需要再进行同步。偏向锁可以提高带有同步但无竞争的程序性能,也就是说他并不一定总是对程序运行有利,如果程序中大多数的锁都是被多个不同的线程访问,那偏向模式就是多余的,在具体问题具体分析的前提下,可以考虑是否使用偏向锁。3.轻量级锁:为了减少获得锁和释放锁所带来的性能消耗,引入了 偏向锁和“轻量级锁,所以在Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁如何在两个线程间共享数据通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAlkawai
44、t/signal/signalAII进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的如何正确的使用wait。?使 用if还是while?wait()方法应该在循环调用,因为当线程获取到CPU开始执行的时候,其他条件可能还没有满足,所以在处理前,循环检测条件是否满足会更好。下面是一段标准的使用wait和notify方法的代码:什么是线程局部变量ThreadLocal线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供ThreadLocal类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如web服务器)使用线
45、程局部变量的时候要特别小 心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java应用就存在内存泄露的风险。ThreadLoal的作用是什么?简单说ThreadLocal就是一种以空间换时间的做法在每个Thread里面维护了一个ThreadLocal.ThreadLocalMap把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了.生产者消费者模型的作用是什么?(1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用(2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和
46、消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约写一个生产者-消费者队列可以通过阻塞队列实现,也可以通过wait-notify来实现.使用阻塞队列来实现使用wait-notify来实现该种方式应该最经典,这里就不做说明了如果你提交任务时,线程池队列已满,这时会发生什么如果你使用的LinkedBlockingQueue,也就是无界队列的话,没 关 系,继续添加任务到阻塞队列中等待执行,因 为LinkedBlockingQueue可以近乎认为是一个无穷大的队列,可以无限存放任务;如果你使用的是有界队列比方说ArrayBlockingQueue 的 话,任务首先会被添加到 Array
47、BlockingQueue 中,ArrayBlockingQueue 满了,则会使用拒绝策略 RejectedExecutionHandler处理满了的任务,默认是AbortPolicyo为什么要使用线程池避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目。java中用到的线程调度算法是什么抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。Thread.sleep(O)的作用是什么由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为
48、了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(O)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。什么是CASCAS,全称为Compare and Swap,即比较-替换。假设有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回true,否则什么都不做并返回false。当然CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,永远是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功什么是乐观锁和悲观锁
49、乐观锁:乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。悲观锁:悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二H 一,直接上了锁就操作资源了。ConcurrentHashMap的并发度是什么?ConcurrentHashM叩的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这也是ConcurrentHashMap 对 Hashtable 的最大优
50、势,任何情况下,Hashtable能同时有两条线程获取Hashtable中的数据吗?ConcurrentHashMap 的工作原理ConcurrentHashMap在jdk 1.6和jdk 1.8实现原理是不同的.jdk 1.6:ConcurrentHashMap是线程安全的,但是与Hashtablea相比,实现线程安全的方式不同。Hashtable是通过对hash表结构进行锁定,是阻塞式的,当一个线程占有这个锁时,其他线程必须阻塞等待其释放锁。ConcurrentHashMap是采用分离锁的方式,它并没有对整个hash表进行锁定,而是局部锁定,也就是说当一个线程占有这个局部锁时,不影响其他线