内推网上投了份简历,先是电话面试半个多小时,一周后通知face to face面试,邮件里面时间是1小时,后来面了接近两个小时,包括linux文件系统、常用命令、服务器监控,java方面包括jvm、jms、多线程、并发、常用框架,DB包括隔离级别、锁、优化等。因为面试岗位是web java高级开发,所以linux、java、DB都有。最后结果还是挂了。
这里记录面试中一个由单件模式扩展的题目,当时答对了前半部分,后半部分在最后问面试官的问题中又请教了他,不过下来试验,发现他的一个答案有错。当时就觉得有问题,只是没有时间细想就结束了。
package com.du.concurrent;
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){
System.out.println("Singleton construct...");
try {
Thread.sleep(500); //线程睡眠0.5s,模拟有些大对象需要长时间construct的过程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Singleton getInstance() {
return instance;
}
public static void main(String[] args) {
System.out.println("Before Singleton construct...");
Singleton.getInstance();
}
}
普通的单件模式代码。其中Thread.sleep(500)用来模拟大对象构建过程中,系统调度构建线程交出CPU时间的过程。
然后被问到假如这个对象很大,不希望在类加载的时候构建对象,希望用一种lazy的方式构建。 1. static对象在类加载时构建,所以上述代码的输出为:
Singleton construct...
Before Singleton construct...
-
lazy的方式:
package com.du.concurrent; public class Singleton { private static Singleton instance = null; private Singleton(){ System.out.println("Singleton construct..."); try { Thread.sleep(500); //线程睡眠0.5s,模拟有些大对象需要长时间construct的过程 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } public static void main(String[] args) { System.out.println("Before Singleton construct..."); Singleton.getInstance(); } }
上述代码输出为:
Before Singleton construct...
Singleton construct...
再被问到上面代码有什么问题,我回答不是线程安全的。然后就需要做同步保护。最直接就是用synchronized保护。不加synchronized的代码:
package com.du.concurrent;
public class Singleton {
private static Singleton instance = null;
private Singleton(){
System.out.println("Singleton construct...");
try {
Thread.sleep(500); //线程睡眠0.5s,模拟有些大对象需要长时间construct的过程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
public void print(String str) {
System.out.println(str);
}
private static final int THREAD_COUNT = 20;
public static void main(String[] args) {
System.out.println("Before Singleton construct...");
Thread[] threads = new Thread[THREAD_COUNT];
for(int i = 0; i < THREAD_COUNT; ++i) {
threads[i] = new Thread(new Runnable() {
public void run() {
Singleton.getInstance().print("Thread run...");
}
});
threads[i].start();
}
if(Thread.activeCount() > 0) {
Thread.yield();
}
}
}
输出结果显示Singleton构建了多次。加了synchronized保护:
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
or
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
结果显示Singleton构建一次。
在用synchronized保护时,面试官提到要讲synchronized细化,我理解上述两种方式效果是一样的,也就没有继续细化的空间,如果只用synchronized保护instance = new Singleton();这行代码,非常明显是达不到效果的。
以上为面试过程中我的答案。最后面试完,问我有没有什么问题,我又请教了这个问题。面试官给的答案:1. 同步可以用volatile修饰对象;2. lazy初始化可以用内部类来实现,因为父类虽然是在加载时就初始化了static对象,但是内部类却是在调用时才初始化。对1,我感觉有问题,因为《深入理解Java虚拟机》中提到,volatile对象需要在更新值时不依赖于其当前的值,但是这里只有当对象为null时才new,也即依赖了当前值。但当时继续下一个问题,来不及细想。对2,则是完全不知道。
问题1的测试代码:
package com.du.concurrent;
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){
System.out.println("Singleton construct...");
try {
Thread.sleep(500); //线程睡眠0.5s,模拟有些大对象需要长时间construct的过程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void print(String str) {
System.out.println(str);
}
private static final int THREAD_COUNT = 20;
public static void main(String[] args) {
System.out.println("Before Singleton construct...");
Thread[] threads = new Thread[THREAD_COUNT];
for(int i = 0; i < THREAD_COUNT; ++i) {
threads[i] = new Thread(new Runnable() {
public void run() {
Singleton.getInstance().print("Thread run...");
}
});
threads[i].start();
}
if(Thread.activeCount() > 0) {
Thread.yield();
}
}
}
输出结果显示的确是construct了多次。
在内部类中初始化:
package com.du.concurrent;
public class Singleton {
private Singleton(){
System.out.println("Singleton construct...");
try {
Thread.sleep(500); //线程睡眠0.5s,模拟有些大对象需要长时间construct的过程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void print(String str) {
System.out.println(str);
}
public static void main(String[] args) {
System.out.println("Before Singleton construct...");
Singleton.InSing.instance.print("Lazy construct...");
}
static class InSing{
public static Singleton instance = new Singleton();
}
}
输出为:
Before Singleton construct...
Singleton construct...
Lazy construct...
construct的确是在进入main函数后才执行。
阿里的java在业界是非常厉害的,很期望能进入阿里,不过可惜...