目錄: 1.StringBuffer和StringBuilder有什麼區別?假設有一個方法,方法內部需要定義一個對象,可能是StringBuffer或StringBuilder,接下來會多次append操作,方法結束時,返回這個對象的toString()結果,並且這個線程會被多線程併發訪問,請選擇這 ...
目錄:
- StringBuffer和StringBuilder有什麼區別?假設有一個方法,方法內部需要定義一個對象,可能是StringBuffer或StringBuilder,接下來會多次append操作,方法結束時,返回這個對象的toString()結果,並且這個線程會被多線程併發訪問,請選擇這個對象是被定義成StringBuffer或者StringBuilder?為什麼?
- synchronized有什麼用?如何使用?(偽代碼,把所有使用方式都分別列出來)
- ReentranLock類有什麼作用?它常用的方法有哪幾個?分別有什麼特點?
- 集群環境下多機器間進行同步操作有什麼可選的解決方案?(最好用偽代碼寫出關鍵部分)
- 列出樂觀鎖的設計要點和使用方法?
- 何為冪等性控制?舉一個例子說明你之前如何實現冪等性控制?(或在項目IDCM中如何實現冪等性控制?)
- spring實現aop用到的關鍵技術是什麼?
- HashMap和ConcurrentHashMap有什麼區別和特點?
- java.util.concurrent package下,你用過哪些類?分別有什麼用途和特點?
- 如果一張表數據量較大,影響了查詢性能,可以有哪些優化方案?建立索引有什麼原則?
- 說一說資料庫事務隔離級別的理解?(項目IDCM中是如何使用的?)
- Spring中註解@Component @Repository @Service @Controller的區別?(項目IDCM中context:component-scan註解掃描是如何配置的?)
- 線程池如何實現?實現線程池的常用的幾個類是怎麼樣的?如何用線程池創建一個單線程?
- 原子操作類AtomicInteger等等這些類的API的瞭解。
1.StringBuffer和StringBuilder有什麼區別?假設有一個方法,方法內部需要定義一個對象,可能是StringBuffer或StringBuilder,接下來會多次append操作,方法結束時,返回這個對象的toString()結果,並且這個線程會被多線程併發訪問,請選擇這個對象是被定義成StringBuffer或者StringBuilder?為什麼?
答:StringBuffer是線程安全的;StringBuilder是線程不安全的。
1.先來看String StringBuffer StringBuilder定義。final修飾的類不能被繼承,即不能擁有子類。 public final class StringBuffer public final class StringBuilder public final class String 2.關於appdend方法的源碼如下: public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } public StringBuilder append(String str) { super.append(str); return this; } 3.對於經常變動的字元串才會考慮使用StringBuilder和StringBuffer,使用StringBuilder效率比StringBuffer高,StringBuffer可以保證線程安全,而StringBuilder不能。
2.synchronized有什麼用?如何使用?(偽代碼,把所有使用方式都分別列出來)
答:synchronized是Java語言的關鍵字,同時也是一個可重入鎖。當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多只有一個線程執行該段代碼。synchronized用於修飾方法和代碼塊。
package basic; public final class TestSynchronized { public static void main(String[] args) { new Thread("線程A") { @Override public void run() { try { print("線程A ..."); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread("線程B") { @Override public void run() { try { print("線程B ..."); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } public static synchronized void print(String str) throws InterruptedException { System.out.println("當前線程:" + Thread.currentThread().getName() + "執行開始"); for (int i = 0; i < 10; i++) { System.out.println(str); Thread.sleep(2000); } System.out.println("當前線程:" + Thread.currentThread().getName() + "執行完畢"); } } // 代碼執行結果: //當前線程:線程A執行開始 //線程A ... //線程A ... //線程A ... //線程A ... //線程A ... //線程A ... //線程A ... //線程A ... //線程A ... //線程A ... //當前線程:線程A執行完畢 //當前線程:線程B執行開始 //線程B ... //線程B ... //線程B ... //線程B ... //線程B ... //線程B ... //線程B ... //線程B ... //線程B ... //線程B ... //當前線程:線程B執行完畢
synchronized在修飾方法的同時,還可以修飾代碼塊,示例代碼如下:
package basic; public class SynchronizedExample { public static void main(String[] args) { new Thread("線程A") { @Override public void run() { print("線程A"); } }.start(); new Thread("線程B") { @Override public void run() { print("線程B"); } }.start(); } public static void print(String str) { System.out.println("線程: " + Thread.currentThread().getName() + "開始執行"); synchronized (SynchronizedExample.class) { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "列印了信息:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } System.out.println("線程: " + Thread.currentThread().getName() + "執行結束"); } } // 代碼執行結果: // 線程: 線程A開始執行 // 線程A列印了信息:0 // 線程: 線程B開始執行 // 線程A列印了信息:1 // 線程A列印了信息:2 // 線程A列印了信息:3 // 線程A列印了信息:4 // 線程A列印了信息:5 // 線程A列印了信息:6 // 線程A列印了信息:7 // 線程A列印了信息:8 // 線程A列印了信息:9 // 線程: 線程A執行結束 // 線程B列印了信息:0 // 線程B列印了信息:1 // 線程B列印了信息:2 // 線程B列印了信息:3 // 線程B列印了信息:4 // 線程B列印了信息:5 // 線程B列印了信息:6 // 線程B列印了信息:7 // 線程B列印了信息:8 // 線程B列印了信息:9 // 線程: 線程B執行結束
接下來詳細說明synchronized在修飾方法的時候的細節。synchronized是對類的當前實例進行加鎖,防止其他線程同時訪問該類的該實例的所有synchronized塊,註意這裡是“類的當前實例”,類的兩個不同實例就沒有這種約束了。那麼static synchronized恰好就是要控制類的所有實例的訪問了,static synchronized是限制線程同時訪問jvm中該類的所有實例同時訪問對應的代碼快。實際上,在類中某方法或某代碼塊中有synchronized,那麼在生成一個該類實例後,該類也就有一個監視快,放置線程併發訪問改實例synchronized保護快,而static synchronized則是所有該類的實例公用一個監視快了,也就是兩個的區別了,也就是synchronized相當於this.synchronized,而static synchronized相當於Something.synchronized。
package basic; public class SynchronizedExample { public static void main(String[] args) { final MySynchronized mySynchronized = new MySynchronized(); new Thread("線程A") { @Override public void run() { mySynchronized.print("線程A"); } }.start(); new Thread("線程B") { @Override public void run() { mySynchronized.print("線程B"); } }.start(); } } class MySynchronized { public synchronized void print(String str) { System.out.println("線程: " + Thread.currentThread().getName() + "開始執行"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "列印了信息:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } System.out.println("線程: " + Thread.currentThread().getName() + "執行結束"); } } // 代碼運行結果: //線程: 線程A開始執行 //線程A列印了信息:0 //線程A列印了信息:1 //線程A列印了信息:2 //線程A列印了信息:3 //線程A列印了信息:4 //線程A列印了信息:5 //線程A列印了信息:6 //線程A列印了信息:7 //線程A列印了信息:8 //線程A列印了信息:9 //線程: 線程A執行結束 //線程: 線程B開始執行 //線程B列印了信息:0 //線程B列印了信息:1 //線程B列印了信息:2 //線程B列印了信息:3 //線程B列印了信息:4 //線程B列印了信息:5 //線程B列印了信息:6 //線程B列印了信息:7 //線程B列印了信息:8 //線程B列印了信息:9 //線程: 線程B執行結束 package basic; public class SynchronizedExample { public static void main(String[] args) { final MySynchronized mySynchronized_first = new MySynchronized(); final MySynchronized mySynchronized_second = new MySynchronized(); new Thread("線程A") { @Override public void run() { mySynchronized_first.print("線程A"); } }.start(); new Thread("線程B") { @Override public void run() { mySynchronized_second.print("線程B"); } }.start(); } } class MySynchronized { public synchronized void print(String str) { System.out.println("線程: " + Thread.currentThread().getName() + "開始執行"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "列印了信息:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } System.out.println("線程: " + Thread.currentThread().getName() + "執行結束"); } } //代碼運行結果: //線程: 線程A開始執行 //線程A列印了信息:0 //線程: 線程B開始執行 //線程B列印了信息:0 //線程A列印了信息:1 //線程B列印了信息:1 //線程A列印了信息:2 //線程B列印了信息:2 //線程A列印了信息:3 //線程B列印了信息:3 //線程A列印了信息:4 //線程B列印了信息:4 //線程A列印了信息:5 //線程B列印了信息:5 //線程A列印了信息:6 //線程B列印了信息:6 //線程A列印了信息:7 //線程B列印了信息:7 //線程A列印了信息:8 //線程B列印了信息:8 //線程A列印了信息:9 //線程B列印了信息:9 //線程: 線程A執行結束 //線程: 線程B執行結束 package basic; public class SynchronizedExample { public static void main(String[] args) { final MySynchronized mySynchronized_first = new MySynchronized(); final MySynchronized mySynchronized_second = new MySynchronized(); new Thread("線程A") { @Override public void run() { mySynchronized_first.print("線程A"); } }.start(); new Thread("線程B") { @Override public void run() { mySynchronized_second.print("線程B"); } }.start(); } } class MySynchronized { public static synchronized void print(String str) { System.out.println("線程: " + Thread.currentThread().getName() + "開始執行"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "列印了信息:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } System.out.println("線程: " + Thread.currentThread().getName() + "執行結束"); } } //代碼運行結果: //線程: 線程A開始執行 //線程A列印了信息:0 //線程A列印了信息:1 //線程A列印了信息:2 //線程A列印了信息:3 //線程A列印了信息:4 //線程A列印了信息:5 //線程A列印了信息:6 //線程A列印了信息:7 //線程A列印了信息:8 //線程A列印了信息:9 //線程: 線程A執行結束 //線程: 線程B開始執行 //線程B列印了信息:0 //線程B列印了信息:1 //線程B列印了信息:2 //線程B列印了信息:3 //線程B列印了信息:4 //線程B列印了信息:5 //線程B列印了信息:6 //線程B列印了信息:7 //線程B列印了信息:8 //線程B列印了信息:9 //線程: 線程B執行結束
上述代碼完整的展示了static synchronized和synchronized的用法。synchronized針對同一個實例不能訪問,針對不同的實例可以同時訪問。static synchronized針對所有的實例均不能同時訪問。synchronized本來就是修飾方法的,後來引申出synchronized修飾代碼塊,只是為了可以更精確的控制衝突限制的訪問區域,使得表現更加高效率。synchronized方法只能鎖定現階段的對象,而synchronized區塊可以鎖定指定的對象,指定的對象直接跟在synchronized()括弧之後。此外,synchronized關鍵字是不能繼承的,也就是說,基類的方法synchronized f(){} 在繼承類中並不自動是synchronized f(){},而是變成了f(){}。繼承類需要你顯式的指定它的某個方法為synchronized方法。還有synchronized不能被繼承,繼承時子類的覆蓋方法必須顯示定義成synchronized。
除了方法前用synchronized關鍵字,synchronized關鍵字還可以用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問。用法是: synchronized(object){/*區塊*/},它的作用域是object對象。當一個線程執行時,將object對象鎖住,另一個線程就不能執行對應的塊。synchronized方法實際上等同於用一個synchronized塊包住方法中的所有語句,然後在synchronized塊的括弧中傳入this關鍵字。當然,如果是靜態方法,需要鎖定的則是class對象。可能一個方法中只有幾行代碼會涉及到線程同步問題,所以synchronized塊比synchronized方法更加細粒度地控制了多個線程的訪問,只有synchronized塊中的內容不能同時被多個線程所訪問,方法中的其他語句仍然可以同時被多個線程所訪問(包括synchronized塊之前的和之後的)。
package basic; public class TestSynchronizedObject { public static void main(String[] args) { final MyObject myObject_first = new MyObject(); final MyObject myObject_seconde = new MyObject(); new Thread("線程A") { @Override public void run() { myObject_first.print("線程A"); } }.start(); new Thread("線程B") { @Override public void run() { /* * 同一個實例,實現了互斥訪問 */ myObject_first.print("線程B"); /* * 不同的實例,並不能夠實現互斥訪問 */ myObject_seconde.print("線程B"); } }.start(); } } class MyObject { /** * * synchronized(this)的用法相當於synchronized直接修飾方法<br/> * * 只針對一個實例的時候有效,針對多個實例的時候無效 */ public void print(String str) { System.out.println("線程" + Thread.currentThread().getName() + "開始執行"); synchronized (this) { for (int i = 0; i < 10; i++) { System.out.println(str + " ." + i + ". "); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } System.out.println("線程" + Thread.currentThread().getName() + "執行結束"); } } package basic; public class TestSynchronizedObject { public static void main(String[] args) { final MyObject myObject_first = new MyObject(); final MyObject myObject_seconde = new MyObject(); new Thread("線程A") { @Override public void run() { myObject_first.print("線程A"); } }.start(); new Thread("線程B") { @Override public void run() { myObject_seconde.print("線程B"); } }.start(); } } class MyObject { /** * * synchronized(MyObject.class)的用法相當於static synchronized修飾方法<br/> * * 在針對多個實例的情況下,互斥有效,但是synchronized括弧後面要指定正確的對象信息 */ public void print(String str) { System.out.println("線程" + Thread.currentThread().getName() + "開始執行"); /* * 1、synchronized(MyObjcet.class)可以正確的實現互斥效果 ,因為調用的是MyObject的對象。 */ synchronized (MyObject.class) { for (int i = 0; i < 10; i++) { System.out.println(str + " ." + i + ". "); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } System.out.println("線程" + Thread.currentThread().getName() + "執行結束"); } }
總結:synchronized方法是一種粗粒度的併發控制,在某一時刻,只能有一個線程執行該synchronized方法。synchronized塊則是一種細粒度的鬢髮控制,只會將塊中的代碼同步,位於方法內,synchronized塊之外的代碼是可以被多個線程同時訪問到的。關於synchronized修飾代碼塊的詳細細節可以參考附錄[1]和附錄[2]。
3.ReentrantLock類有什麼作用?它常用的方法有哪幾個?分別有什麼特點?(讀法:Re-entrantLock)
上面的的synchronized和ReentranLock是最經典的可重入鎖,在面試中經常有問到兩種鎖的對比。先介紹ReentranLock的使用和代碼示例,接著再分析ReentranLock和synchronized的具體區別。
4.集群環境下多機器間進行同步操作有什麼可選的解決方案?(最好用偽代碼寫出關鍵部分)
reids
5.列出樂觀鎖的設計要點和使用方法?
6.何為冪等性控制?舉一個例子說明你之前如何實現冪等性控制?(或在項目IDCM中如何實現冪等性控制?)
7.spring實現aop用到的關鍵技術是什麼?
當然是動態代理(這個網易面試面過的)。引申一下JDK中java提供的動態代理的應用,靜態代理和動態代理的關鍵區別。公共的介面subject,然後有委托類RealSubject和代理類ProxySubject。正所謂靜態代理只是在編譯階段就確定了代理類和委托來之間的關係。而動態代理則是,在運行時,才能確定具體的代理類和委托類之間的關係,因此寫此部分代碼要結合JDK的反射機制來實現。(代理介面)。
代碼是死的,進程才是活的。所以說,運行時確定的委托類和代理類之間的關係,是根據運行時來決定的。
8.HashMap和ConcurrentHashMap有什麼區別和特點?
9.java.util.concurrent package下,你用過哪些類?分別有什麼用途和特點?
10.如果一張表數據量較大,影響了查詢性能,可以有哪些優化方案?建立索引有什麼原則?
11.說一說資料庫事務隔離級別的理解?(項目IDCM中是如何使用的?)
12.Spring中註解@Component @Repository @Service @Controller的區別?(項目IDCM中context:component-scan註解掃描是如何配置的?)
13.線程池如何實現?實現線程池的常用的幾個類是怎麼樣的?如何用線程池創建一個單線程?
14.原子操作類AtomicInteger等等這些類的API的瞭解?
附錄:
[1] https://segmentfault.com/q/1010000005945389?_ea=960340
[2] https://segmentfault.com/q/1010000005944096?_ea=959633
[3]