1.
private static final String msg = "taobao"; public static void main(String[] args) {
String a = "tao" + "bao";
String b = "tao" ;
String c = "bao";
System.out.println(a==msg);//true
System.out.println((b+c)==msg);//false
}
- 悲观锁和乐观锁的区别,分别是什么?
悲观锁:一段执行逻辑加上悲观锁,不同线程同时执行时,只有一个线程执行,其他的线程在入口处等待,直到锁被释放。
乐观锁:一段执行逻辑加上乐观锁,不同线程同时执行时,可以同时执行,在最后更新数据的时候要检查这些数据是否被其他线程修改了(版本和执行初是否相同),没有修改则更新,否则放弃本次操作。
当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
-
a) 运行结果是
i.ii. 因为B为A的子类,抛出B的时候Exception A可以捕获到,接着抛出a,这里的a为形参,实际是一个B类的对象,因此下方Exception依旧能捕获到,最后执行finally块。
- ArrayList和数组(Array)和有什么区别?什么时候应该使用Array而不是ArrayList?
a) Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
b) Array是指定大小的,而ArrayList大小是固定的。
c) Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。尽管ArrayList明显是更好的选择,但也有些时候Array比较好用。
d) (1)如果列表的大小已经指定,大部分情况下是存储和遍历它们。
e) (2)对于遍历基本数据类型,尽管Collections使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。
f) (3)如果你要使用多维数组,使用[][]
- 简述BIO、NIO和AIO的区别及其适用场景
a) Java AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
b)
c) NIO比BIO的改善之处是把一些无效的连接挡在了启动线程之前,减少了这部分资源的浪费(因为我们都知道每创建一个线程,就要为这个线程分配一定的内存空间)
d) AIO比NIO的进一步改善之处是将一些暂时可能无效的请求挡在了启动线程之前,比如在NIO的处理方式中,当一个请求来的话,开启线程进行处理,但这个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被阻塞了。
e)
f) 适用场景分析:
g) BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解,如之前在Apache中使用。
h) NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持,如在 Nginx,Netty中使用。
i) AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持,在成长中,Netty曾经使用过,后来放弃。
- 简述内存泄漏与溢出的区别?何时产生内存泄漏?
a) 些连接就不能被GC回收而导致内存泄漏。一般情况下,在try代码块里创建连接,在finally里释放连接,就能够避免此类内存泄漏。
b) 4) 外部模块的引用:调用外部模块的时候,也应该注意防止内存泄漏。如模块A调用了外部模块B的一个方法,如:public void register(Object o)。这个方法有可能就使得A模块持有传入对象的引用,这时候需要查看B模块是否提供了去除引用的方法,如unregister()。这种情况容易忽略,而且发生了内存泄漏的话,比较难察觉,应该在编写代码过程中就应该注意此类问题。
c) 5) 单例模式:使用单例模式的时候也有可能导致内存泄漏。因为单例对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象(生命周期比较短)的引用,那么这个外部对象就不能被回收,而导致内存泄漏。如果这个外部对象还持有其它对象的引用,那么内存泄漏会更严重,因此需要特别注意此类情况。这种情况就需要考虑下单例模式的设计会不会有问题,应该怎样保证不会产生内存泄漏问题。
- 什么是线程死锁?死锁如何产生?如何避免线程死锁?
a) 死锁的介绍:
b) 线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。
c)
d) 死锁的产生的一些特定条件:
e) 1、互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 。
f) 2、请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
g) 3、不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用。
h) 4、循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。
i) 如何避免:
j) 1、加锁顺序:
k) 当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。当然这种方式需要你事先知道所有可能会用到的锁,然而总有些时候是无法预知的。
l) 2、加锁时限:
m) 加上一个超时时间,若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。但是如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。
n) 3、死锁检测:
o) 死锁检测即每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景
- Java反射机制中,class.forName()和classloader的区别?
a) class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象
- 运行结果
public static void main(String[] args) {
String a = null;
a+= "aaa";
System.out.println(a);
}