内推網上投了份履歷,先是電話面試半個多小時,一周後通知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在業界是非常厲害的,很期望能進入阿裡,不過可惜...