【java】java面试题经典汇总,包括并发多线程

来源:互联网 发布:致远软件待遇 编辑:程序博客网 时间:2024/06/08 00:23

近期一直在找java的工作,除了认真回顾《TIJ》的笔记,准备点java面试题也是很重要的。

下面放上的东西大都是网上可以搜索的到的,经过我的阅读我把他们精简了一下,变成了一个个的知识点。

过两天再写关于java内存模型和java垃圾回收机制的总结,这2个点也是面试常常出现的。


java面试题汇总

1.     面向对象的特点:

a)    抽象:数据抽象和行为抽象。

b)    继承

c)     封装

d)    多态:方法重载编译时多态,方法重写运行时多态。

2.     修饰符    当前类    同 包     子 类     其他包

public       √        √       √         √

protected  √        √     √         ×

default     √        √      ×         ×

private     √     ×      ×         ×

3.     Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean。java中:基本类型、枚举类型、引用类型。

4.     错误写法:float f=3.4。因为3.4是双精度,正确写法:
          floatf =(float)3.4;       或float f =3.4F。

5.     short s1 = 1; s1 = s1 + 1;    错误,窄化需要强制转换。
short s1 = 1; s1 += 1;       正确,隐含强转s1 = (short)(s1 + 1);

6.     goto和const是java中的保留字,有特殊意义,在目前java中没有使用。

7.     自动装箱和自动拆箱:    

a)    Integera = new Integer(3);    Integer b = 3; // 自动装箱         int c = 3;

       System.out.println(a == b);    //false 两个引用没有引用同一对象

System.out.println(a== c);     // true a自动拆箱成int类型再和c比较

b)    Integerf1 = 100, f2 = 100, f3 = 150, f4 = 150;

       System.out.println(f1 == f2); //true

       System.out.println(f3 == f4);  //false

分析:当自动装箱时会调用valueOf(),查看源码可以知道如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象。

8.     &按位与,&&短路与(第一个为假,第二个条件就不再判断)

9.     栈:基本类型,对象引用,执行环境上下文,线程私有。
堆:new出来的对象,被所有线程共享
方法区:类信息、常量、静态变量、编译后的代码等数据

a)    特殊例子1:int a=3;编译器会先看栈中是否已存在3,若有引用a就指向3,否则把3存放进栈,在a指向3。

b)    特殊例子2:String s = new String(“abc”);     s存放在栈上,“abc“静态区。
 String a = ”abc“;    String b = ”abc“; a == b为true, s == b为false 

10.  Math.round(11.5) = 12
Math.round(-11.3) = -11    Math.round(-11.9)= -12
四舍五入的原理是在参数上加0.5然后下取整(floor()取最接近小于天花板的那个数)。

11.  switch(expr)  expr目前可以是除long之外的基本类型,甚至是String、enum。

12.  31* num 等价于(num<< 5) - num,素数可以使哈希冲突尽量减少,但是如今的java散列函数都使用2的整数次方,目的是用掩码代替除法。

13.  构造器不能被继承,因此不能被重写,只能重载。

14.  如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。

15.  String类是一个final类,不能被继承。

a)    并且String是只读字符串。

b)    StringBuilder用于单线程中,对字符串的操作。

c)     StringBuffer可用于多线程中,为线程安全的StringBuilder版本。

d)    显式的使用StringBuilder相比较+可以大大减少产生的StringBuilder次数。

16.  java的方法调用只支持值传递。因此传入的引用是对调用者来说是无法改变的。类比C++的引用传递。

17.  仅用返回值无法区分重载,此时编译器无法无法明确判断出语义。
int submit();  char submit();    int a = submit();编译器调用哪个?

18.   JVM加载class文件的原理机制:
答:JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的。经过编译的Java源文件是一个或多个类文件。
当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化
类加载过程采取了父亲委托机制(PDM),PDM更好的保证了Java平台的安全性。

19.  java中的char变量可以储存一个汉字,因其默认编码是unicode,一个char两字节。

20.  抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量(static final)。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。

21.  静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。
class Outer {

 class Inner {}

 public static void foo() { new Inner(); }  //静态方法没有this指针,编译错误

 public void bar() { new Inner(); }

 public static void main(String[] args) {newInner();}

}     

如果要在静态方法中创建内部类对象,可以这样做:new Outer().new Inner();

 

22.  java实际开发中也有可能会导致内存泄露:可能会存在无用但可达的对象。还有无意识的对象保持也可能引发内存泄露,从而对性能造成重大影响,造成OutofMemoryError

23.  抽象方法不能是静态的,因为静态方法不能被重写;抽象方法同时不能是本地的,因为抽象方法是没有实现的;抽象方法也不能是synchronized,因此它要求实现细节。

24.  静态方法中不能直接调用非静态方法,除非通过创建一个对象,通过对象间接访问。

25.  克隆对象的两种方式;

a)    实现Cloneable接口并重写Object类中的clone()方法。

b)    实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以在编译时检查出要克隆的对象是否支持序列化,不是在运行时。

26.  String s = new String("xyz");创建了两个字符串对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。

27.  接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。

28.  一个.java文件可以包含多个类,但只能有一个public,且要与文件名相同。

29.  Java中的final关键字有哪些用法?
(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

30.  数据类型之间的转换:

a)    如何将字符串转换为基本数据类型?调用基本数据类型对应的包装类中的方法parseXXX(String)

b)    如何将基本数据类型转换为字符串?一种方法是将基本数据类型与空字符串("")连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf()方法返回相应字符串

31.  字符串反转:public static String reverse(String originStr) {

                      if(originStr== null || originStr.length() <= 1)

                           returnoriginStr;

             return reverse(originStr.substring(1)) +originStr.charAt(0);

}

32.  GB2312编码的字符串转换为ISO-8859-1编码的字符串:
Strings2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");

33.  日期和时间的类:

a)    java.util.Calendar,LocalDateTime(javase8),System.getnanotime().

b)    利用java.text.DataFormat 的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。Java 8中可以用java.time.format.DateTimeFormatter来格式化时间日期。

34.  Java特别适合于互联网应用程序开发;而JavaScript是一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言。比较:

a)    前者面向对象,后者基于对象。

b)    前者执行前必须编译,后者由浏览器解释执行。

c)     Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。 

d)    java为静态语言,后者为动态语言

35.  断言检查通常在开发和测试时开启,软件发布后通常是关闭。
assert Expression1;     表达式1为假就抛出一个AssertionError
assert Expression1 : Expression2 ;   表达式2可以是得出一个值的任意表达式;这个值用于生成显示更多调试信息的字符串消息

36.  Error和Exception的区别:Error表示系统级的错误和程序不必处理的异常,有可能恢复但很困难比如:内存溢出。Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题。

37.  在return之前,finally中的语句总会执行,甚至是return在try块中。不要在finally中返回值。

38.  运行时异常和受检异常。运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。受检异常必须在方法声明,运行时异常不需要声明就可以抛出。

39.  常见的运行时异常:- ArithmeticException、ClassCastException、   IllegalArgumentException IndexOutOfBoundsException、 NullPointerException、

40.   finally通常放在try…catch…的后面构造总是执行代码块。 

41.  finalize:定义在Object类,这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。

42.  catch中基类类型的异常可以捕获子类的异常。

43.  List、Set 继承自Collection,Map 不是。

44.  Collection和Collections的区别? Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

45.  TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素? TreeSet和TreeMap要求存放的对象所属的类必须实现Comparable接口,并提供compareTo()方法,当插入元素时会回调该方法比较元素的大小。Collections.sort()方法有两种重载的形式,第一种要求其中存放对象实现Comparable接口。第二种要求传入第二个参数Comparator接口的子类型(需要重写compare方法实现元素的比较),通过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。


 

46.  sleep()和wait()的区别:前者是Thread类的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。

47.  线程的sleep()方法和yield()方法有什么区别?

① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;

② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;

③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;

④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性

48.  同步和异步:所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作。

49.  启动一个线程需要start()方法,run()方法是线程启动后要进行回调的方法。

50.  

51.      public static void fileCopyNIO(String source, String target) throws IOException {

            FileInputStream in = newFileInputStream(source);

         FileOutputStream out = newFileOutputStream(target);

         FileChannel inChannel = in.getChannel();

         FileChannel outChannel =out.getChannel();

         ByteBuffer buffer =ByteBuffer.allocate(4096);

                while(inChannel.read(buffer) !=-1) {

                    buffer.flip();

                    outChannel.write(buffer);

                    buffer.clear();}

     }   //拷贝文件

52.      public static int countWordInFile(Stringfilename, String word) {

        int counter = 0;

        FileReader fr = newFileReader(filename);

        BufferedReader br = newBufferedReader(fr);

                String line = null;

                while ((line = br.readLine())!= null) {

                    int index = -1;

                    while (line.length() >=word.length() && (index = line.indexOf(word))>= 0) {

                        counter++;

                        line = line.substring(index + word.length());

                                  }

}

}                //确定一个文件中指定字符串出现的整数

53.  XML的主要作用有两个方面:数据交换和信息配置。在做数据交换时,XML将数据用标签组装成起来,然后压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再从XML文件中还原相关信息进行处理,XML曾经是异构系统间交换数据的事实标准,但此项功能几乎已经被JSON(JavaScript Object Notation)取而代之。当然,目前很多软件仍然使用XML来存储配置信息,我们在很多项目中通常也会将作为配置信息的硬代码写在XML文件中,Java的很多框架也是这么做的,而且这些框架都选择了dom4j作为处理XML的工具,因为Sun公司的官方API实在不怎么好用。

54.  事务的ACID是指什么?

a)    原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;

b)    一致性(Consistent):事务结束后系统状态是一致的;

c)     隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;

d)    持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。

55.  java对正则表达式如何支持:

a)    Java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。

b)    此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作,请参考下面面试题的代码。

56.  获得一个类的类对象有哪些方式?

a)    方法1:类型.class,例如:String.class

b)    方法2:对象.getClass(),例如:"hello".getClass()

c)     方法3:Class.forName(),例如:Class.forName("java.lang.String")

 

57.  北京市(朝阳区)(西城区)(海淀区),截取结果为:北京市 
对应需要的正则表达式: .*?(?=\\()
(?=\\()表示匹配(之前的内容。(本来有特殊意义。

58.  如何通过反射创建对象?

a)    方法1:通过类对象调用newInstance()方法,例如:String.class.newInstance()

b)    方法2:通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器(Constructor)对象并调用其newInstance()方法创建对象,例如:String.class.getConstructor(String.class).newInstance("Hello");

59.  通过反射调用对象的方法:
 String str = "hello";
 Method m = str.getClass().getMethod("toUpperCase");
 System.out.println(m.invoke(str)); // HELLO

60.  单例模板:public class Singleton {
              private Singleton(){}
              private static Singleton instance= new Singleton();
              public static Singleton getInstance(){ returninstance;
          }

 

终极列表部分:

61.  java虚拟机是一个可以执行java字节码的虚拟机进程,java源文件编译成字节码。

62.  JRE意思是java运行时环境,就是将执行java程序的虚拟机。JDK包括了JRE,编译器和javaDoc,java调试器等工具。

63.  static方法不能被覆盖(overriding),因为他是编译时静态绑定,不和任何一个具体类相关,而方法重写是运行时绑定的。

64.  java中的char一般为2字节,long为8字节。

65.  类必须实现接口的所有方法,类继承了抽象类则可以不用这样(因此这个类也要是抽象的)。抽象类和接口是不能被实例化的,但是抽象类中有main这个类是可以运行的。

66.  java函数调用全都是值传递。

67.  为什么集合类不实现Serializable接口?因为序列化是与集合的具体实现相关的。

68.  Iterator和ListIterator的区别是什么?

a)    前者能用于Set和List,后者只能用于List

b)    前者只能往后遍历,后者可以双向

c)     后者实现了Iterator接口,包含更多的方法,比如替换,添加等。

69.  快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的,而java.util.concurrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出
ConcurrentModificationException异常,安全失败的迭代器永不会抛出这样的异常。

70.  HashMap中的对象要实现hashCode()方法和equals()方法(要用instanceof同时判断不为null和为某个类实例,判断某个条件是否相等)。put的时候HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。

71.  HashMap允许键和值是null,而Hashtable不允许键或者值是null。

HashMap更适合于单线程环境,而Hashtable适合于多线程环境。

72.  Array和ArrayList的区别:

a)    前者大小固定,后者可以动态调整。

b)    前者可以放基本类型和对象,后者只能放对象。

c)     后者提供了更多操作比如添加删除等。

d)    对基本类型,集合可以自动装箱,但对固定大小的数据,此方式较慢。

73.  PriorityQueue默认自然排序。不接受null,因为没有对应比较器,且非线程安全。

74.  根据应用的需要正确选择要使用的集合的类型对性能非常重要,比如:假如元素的大小是固定的,而且能事先知道,我们就应该用Array而不是ArrayList。

75.  Java集合类框架的最佳实践有哪些?

a)    根据应用的需要正确选择要使用的集合的类型对性能非常重要,比如:假如元素的大小是固定的,而且能事先知道,我们就应该用Array而不是ArrayList。

b)    有些集合类允许指定初始容量。因此,如果我们能估计出存储的元素的数目,我们可以设置初始容量来避免重新计算hash值或者是扩容。

c)     为了类型安全,可读性和健壮性的原因总是要使用泛型。同时,使用泛型还可以避免运行时的ClassCastException。

d)    使用JDK提供的不变类(immutable class)作为Map的键可以避免为我们自己的类实现hashCode()和equals()方法。

e)    编程的时候接口优于实现。

f)     底层的集合实际上是空的情况下,返回长度是0的集合或者是数组,不要返回null。

76.  Enumeration接口和Iterator接口的区别有哪些?

a)    前者速度较后者为2倍,消耗内存更小。

b)    二者功能重复,但是后者线程安全,并允许调用者删除操作,前者无法这样。

77.  如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?

不会,在下一个垃圾回收周期中,这个对象将是可被回收的。

78.  java中数组比较特殊,它是属于数组原本类型的一个特殊子类。因此Obeject[]要求的范围比Object要小,重载,null作形参时调用Object[]
注意:数组引用存放在栈内存,数组元素是存放在堆内存中,通过栈内存中的指针指向对应元素在堆内存中的位置来实现访问。

79.  串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?

吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概100M左右的内存)就足够了。

并发面试题汇总:

1.     Java中Runnable和Callable有什么不同?
Callable要实现call方法,他可以返回值和抛出异常这是run方法做不到的。exec.submit()接受实现Callable对象,调用其中的call方法返回一个Future值。

2.     Java内存模型规定和指引Java程序在不同的内存架构、CPU和操作系统间有确定性地行为。它在多线程的情况下尤其重要。Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。这个关系定义了一些规则让程序员在并发编程时思路更清晰。比如,先行发生关系确保了:

a)    线程内的代码能够按先后顺序执行,这被称为程序次序规则。

b)    对于同一个锁,一个解锁操作一定要发生在时间上后发生的另一个锁定操作之前,也叫做管程锁定规则。

c)     前一个对volatile的写操作在后一个volatile的读操作之前,也叫volatile变量规则。

d)    一个线程内的任何操作必需在这个线程的start()调用之后,也叫作线程启动规则。

e)    一个线程的所有操作都会在线程终止之前,线程终止规则。

f)     一个对象的终结操作必需在这个对象构造完成之后,也叫对象终结规则。

g)    可传递性     //阅读java并发编程实战第十六章

3.     线程安全函数包括可重入函数。

a)    可重入函数:可以被中断的,结果不受影响,不包含全局变量或static变量等。

b)    线程安全函数:当被多个线程多次调用时,产生的结果与单个线程一致。

4.     竞态条件:一个设备或系统(比如通过线程)试图同时执行两个操作的时候出现的不希望的状态。如何要先执行的程序被放在后面执行,可能会出现bug,比如大量读写。

5.     java停止线程:java以前有显式的stop()等函数停止线程但因潜在死锁的原因目前被启用了,通常run()方法或者call()方法结束就意味着线程的结束,我们通常用一个volatile布尔变量来退出run或call方法的循环。
shutdownNow向所有线程发送一个Interrupt信号,但是不一定能被关闭,得具体看。

6.     当一个线程发生异常时,若异常未被处理线程将终止。另一方面,我们可以实现
实现Thread.UncaughtExceptionHandler接口,并实现其中的

uncaughtException(Threadt, Throwable e) 方法。最后还要:
t.setUncaughtExceptionHandler(Myexceptionclassname);

7.     两个线程间如何共享数据:通过共享对象,或者通过阻塞队列。

8.     为什么wait,notify,notifyAll不再Thread类中,而在Object中:
java中的锁是对象级的,通过某个线程获取某个对象的锁,如果wait定义在Thread类,那么线程等待的是哪个锁就不明显了。wait会释放当前对象的锁。

9.     ThreadLocal线程本地存储:为使用相同变量的每个线程都创建一个不同的存储,解决了线程安全的问题。为每个提供自己独立的存储,将大大提高效率。

10.  java的interrupted和isinterrupted的区别:前者会清除中断标志,后者不会。调用Thread.interrupt()中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。一个线程的中断状态有可能被其他线程所改变,所以用前面的相对好一点。

11.  wait和notify要求在静态块中使用,一方面试API要求,另一方面防止二者出现竞态条件。

12.  为什么应该在循环里等待wait?

a)    多个任务处于不同原因可以都在wait,但是第一个notifyAll会唤醒所有任务,如果不是我们感兴趣的条件应该继续wait。

b)    wait正确被唤醒时,可能此时这个任务不能再被执行,应当再次投入wait的情况。

总结:当一个等待线程醒来时,不能认为它原来的等待状态仍然是有效的,在notify()方法调用之后和等待线程醒来之前这段时间它可能会改变。

13.  java同步集合和并发集合的区别:

a)    并发集合的可扩展性更高, JDK1.5以前使用同步集合会导致争用。

b)    Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。

14.  java中栈和堆的区别:
每个线程拥有自己的独立栈,包含了本地变量,函数参数,方法参数和栈调用。而堆是所有线程所共享的,每个线程为提升效率会缓存一部分堆到自己的栈中,访问注意加锁。

15.  TIJP709生产者与消费者wait和notifyAll实现。也可以用Semaphore 或者 BlockingQueue来实现。

16.  死锁与饥饿:死锁是同步的,饥饿时异步。饥饿的意思是一个线程(进程)在无限的等待另外两个或多个线程(进程)占有的但是不会释放的资源。

17.  活锁和死锁的主要区别:活锁可以看做特殊的饥饿,其状态是可以不断改变的,而死锁的状态是不能改变的。比如一个狭小的走廊,两人避让的方向相同但还是谁都过不去。

18.  Thread. holdsLock()可以检查当前对象是否拥有锁。

19.  如何让三个线程T1,T2,T3按照顺序执行?用join函数,依次创建T3,T2,T1函数,并且T3 T2依次调用t2.join()  t1.join(当前线程挂起知道t2线程完成)

20.  yield暂停当前线程,让其他具相同优先级线程先运行,但不能保证一定能运行。

21.  semaphore计数信号量,acquire()方法和release()方法。应用:数据库连接池。

22.  提高一个任务时,线程池队列已满,不会阻塞等待!!而是由submit()方法将会抛出一个RejectedExecutionException异常。

23.  Immutable对象可以在没有同步的情况下共享,降低了对该对象进行并发访问时的同步化开销。

24.  多线程中利用while的忙等,保留了CPU的控制权,避免了CPU缓存重建。

25.  volatile保证了可视性,但是无法保证原子性,Atomic类保证了原子性。

26.  同步块中抛出异常也会导致锁的正常释放。

27.  wait()更多的是用作线程间通信并释放锁等待其他线程唤醒,仅优先级相同或更高的任务可获得CPU,sleep会让出CPU,但是不会释放锁。

28.  单例模式的双检锁:

29.  如何在Java中创建线程安全的Singleton?


0 0
原创粉丝点击