1. 昆明java培訓:java要學到什麼程度才能勝任工作
Java作為面向對象編程語言,不僅吸收了C++語言的各種優點,還摒棄了C++里難以理解的多繼承、指針等概念,因此Java語言具有功能強大和簡單易用的兩個特徵。
也正是由於其簡單性、可移植性、安全性、動態性等特點,Java成為了伺服器端的頂樑柱,保護著網站的一方安全。
既然Java語言這么優秀,那麼,如果想要以java去找工作,一個Java初學者要學多少Java知識,才能找到第一份Java工作呢?首先我們先要了解一下在工作過程中需要用到的java基礎知識有哪些:1、synchronizedstatic修飾類和方法有什麼區別?2、HashMap的原理,底層數據結構,rehash的過程,指針碰撞問題?3、HashMap的線程安全問題,為什麼會產生這樣的線程安全問題?3、ConcurrentHashMap的數據結構,底層原理,put和get是否線程安全?5、JavaIO的一些內容,包括NIO,BIO等?如果以上的基礎知識大家能夠運用自如,也就是說工作過程中做一些基本的事情還是可以的,但是是遠遠不過的,如果想要在工作過程中有自己的一席之地還是要學會以下幾點,才能夠勉強進入企業工作。
一、Java線程池的構造方法,裡面參數的含義,以及原理:1、volatile和ThreadLocal解決了什麼問題2、CAS在Java中的具體實現3、Java虛擬機的構成,以及一個Java對象的生命周期,還有堆棧和方法區中存儲的內容4、JVM的GC過程,包括一些實際問題的分析,比如說明一個現象,讓你分析可能是什麼原因會導致這樣的問題,應該如何對JVM參數進行調優5、synchronized和Lock的區別,以及底層實現原理6、FullGC和MinorGC觸發的條件7、GCRoots的選擇8、jmap,jstat,jstack等的使用場景,MAT等9、ClassLoader的載入過程10、CountDownLatch、CyclicBarrier和Semaphore等11、Java8的新特性等二、資料庫:這里的資料庫包含兩種,一種一般是MySQL,另外是NoSql資料庫,包括Redis、MongoDB等。
一般會問的問題有:1、innerjoin和leftjoin等的區別2、SQL調優,explain,profile等3、InnoDB和Myisam的區別4、ACID5、資料庫的事務隔離級別,以及他們分別能解決什麼問題6、Redis的幾種數據結構7、Redis是單線程還是多線程8、Redis的持久化9、悲觀鎖和樂觀鎖的含義10最左前綴索引,索引的數據結構,聚簇索引等(這塊還沒搞明白)三、框架因為spring是我們常用的框架,所以這塊的內容會問的比較多,也會比較細。
1、Spring的兩大特性(IoC和AOP)2、Spring的bean的生命周期3、Spring是如何解決Bean的循環引用問題的4、AOP的兩種實現方式,以及兩者的區別(這里其實使用了動態代理,具體動態代理分為兩種,一種是JDK的動態代理,主要使用的是JDK的反射,還有一種是CGLib,兩者區別可以自己搜索,文章比較多)5、AOP一般的使用場景6、Spring的事務原理MyBatis:這塊問到的比較簡單些:1、$和#的區別2、MyBatis和Hibernate的區別3、源碼,一般問的比較少Dubbo:因為平時自己用到了Dubbo,所以這塊會有問到:1、RPC的原理2、Dubbo是如何完成遠程調用的3、Dubbo如何進行調優4、Dubbo的通信協議5、Dubbo是如何實現負載均衡的ZooKeeper:1、ZK的使用場景2、ZK的選舉機制3、ZK的節點類型4、一致性Hash原理數據結構和演算法:這塊的內容是基礎,如果面試官懷疑你的能力,一般一會問到這部分內容,比如樹的遍歷、快速排序等。
linux:一般會問一些命令的使用,然後會舉一個實際的場景,讓你用命令去排查問題,這塊自己不是很熟,需要盡快加強。
隨著java的普及,懂Java的人越來越多,企業也會對求職者提出更高的要求,他們更希望招聘一些馬上能上手工作的,所以傾向於招聘一些有項目開發經驗的,這也是為什麼那麼多的大學計算機專業畢業的大學生找不到工作的原因,所以越來越多的大學生才會選擇畢業前後參加一些專業的Java培訓班來增加實戰經驗。
只有增加自我實力才能出於不敗之地。
2. java什麼是樂觀鎖什麼是悲觀鎖
保證數據安全,處理多用戶並發訪問。悲觀鎖,鎖如其名,他對世界是悲觀的,他認為別人訪問正在改變的數據的概率是很高的,所以從數據開始更改時就將數據鎖住,知道更改完成才釋放。樂觀鎖,他對世界比較樂觀,認為別人訪問正在改變的數據的概率是很低的,所以直到修改完成准備提交所做的的修改到資料庫的時候才會將數據鎖住。完成更改後釋放。悲觀鎖會造成訪問資料庫時間較長,並發性不好,特別是長事務。樂觀鎖在現實中使用得較多,廠商較多採用。
3. Java鎖有哪些種類,以及區別
一、公平鎖/非公平鎖
公平鎖是指多個線程按照申請鎖的順序來獲取鎖。
非公平鎖是指多個線程獲取鎖的順序並不是按照申請鎖的順序,有可能後申請的線程比先申請的線程優先獲取鎖。有可能,會造成優先順序反轉或者飢餓現象。
對於Java ReentrantLock而言,通過構造函數指定該鎖是否是公平鎖,默認是非公平鎖。非公平鎖的優點在於吞吐量比公平鎖大。
對於Synchronized而言,也是一種非公平鎖。由於其並不像ReentrantLock是通過AQS的來實現線程調度,所以並沒有任何辦法使其變成公平鎖。
二、可重入鎖
可重入鎖又名遞歸鎖,是指在同一個線程在外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖。說的有點抽象,下面會有一個代碼的示例。
對於Java ReentrantLock而言, 他的名字就可以看出是一個可重入鎖,其名字是Re entrant Lock重新進入鎖。
對於Synchronized而言,也是一個可重入鎖。可重入鎖的一個好處是可一定程度避免死鎖。
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}
上面的代碼就是一個可重入鎖的一個特點,如果不是可重入鎖的話,setB可能不會被當前線程執行,可能造成死鎖。
三、獨享鎖/共享鎖
獨享鎖是指該鎖一次只能被一個線程所持有。
共享鎖是指該鎖可被多個線程所持有。
對於Java
ReentrantLock而言,其是獨享鎖。但是對於Lock的另一個實現類ReadWriteLock,其讀鎖是共享鎖,其寫鎖是獨享鎖。
讀鎖的共享鎖可保證並發讀是非常高效的,讀寫,寫讀 ,寫寫的過程是互斥的。
獨享鎖與共享鎖也是通過AQS來實現的,通過實現不同的方法,來實現獨享或者共享。
對於Synchronized而言,當然是獨享鎖。
四、互斥鎖/讀寫鎖
上面講的獨享鎖/共享鎖就是一種廣義的說法,互斥鎖/讀寫鎖就是具體的實現。
互斥鎖在Java中的具體實現就是ReentrantLock
讀寫鎖在Java中的具體實現就是ReadWriteLock
五、樂觀鎖/悲觀鎖
樂觀鎖與悲觀鎖不是指具體的什麼類型的鎖,而是指看待並發同步的角度。
悲觀鎖認為對於同一個數據的並發操作,一定是會發生修改的,哪怕沒有修改,也會認為修改。因此對於同一個數據的並發操作,悲觀鎖採取加鎖的形式。悲觀的認為,不加鎖的並發操作一定會出問題。
樂觀鎖則認為對於同一個數據的並發操作,是不會發生修改的。在更新數據的時候,會採用嘗試更新,不斷重新的方式更新數據。樂觀的認為,不加鎖的並發操作是沒有事情的。
從上面的描述我們可以看出,悲觀鎖適合寫操作非常多的場景,樂觀鎖適合讀操作非常多的場景,不加鎖會帶來大量的性能提升。
悲觀鎖在Java中的使用,就是利用各種鎖。
樂觀鎖在Java中的使用,是無鎖編程,常常採用的是CAS演算法,典型的例子就是原子類,通過CAS自旋實現原子操作的更新。
六、分段鎖
分段鎖其實是一種鎖的設計,並不是具體的一種鎖,對於ConcurrentHashMap而言,其並發的實現就是通過分段鎖的形式來實現高效的並發操作。
我們以ConcurrentHashMap來說一下分段鎖的含義以及設計思想,ConcurrentHashMap中的分段鎖稱為Segment,它即類似於HashMap(JDK7與JDK8中HashMap的實現)的結構,即內部擁有一個Entry數組,數組中的每個元素又是一個鏈表;同時又是一個ReentrantLock(Segment繼承了ReentrantLock)。
當需要put元素的時候,並不是對整個hashmap進行加鎖,而是先通過hashcode來知道他要放在那一個分段中,然後對這個分段進行加鎖,所以當多線程put的時候,只要不是放在一個分段中,就實現了真正的並行的插入。
但是,在統計size的時候,可就是獲取hashmap全局信息的時候,就需要獲取所有的分段鎖才能統計。
分段鎖的設計目的是細化鎖的粒度,當操作不需要更新整個數組的時候,就僅僅針對數組中的一項進行加鎖操作。
七、偏向鎖/輕量級鎖/重量級鎖
這三種鎖是指鎖的狀態,並且是針對Synchronized。在Java
5通過引入鎖升級的機制來實現高效Synchronized。這三種鎖的狀態是通過對象監視器在對象頭中的欄位來表明的。
偏向鎖是指一段同步代碼一直被一個線程所訪問,那麼該線程會自動獲取鎖。降低獲取鎖的代價。
輕量級鎖是指當鎖是偏向鎖的時候,被另一個線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,提高性能。
重量級鎖是指當鎖為輕量級鎖的時候,另一個線程雖然是自旋,但自旋不會一直持續下去,當自旋一定次數的時候,還沒有獲取到鎖,就會進入阻塞,該鎖膨脹為重量級鎖。重量級鎖會讓其他申請的線程進入阻塞,性能降低。
八、自旋鎖
在Java中,自旋鎖是指嘗試獲取鎖的線程不會立即阻塞,而是採用循環的方式去嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗,缺點是循環會消耗CPU。
典型的自旋鎖實現的例子,可以參考自旋鎖的實現
4. 深入研究 Java Synchronize 和 Lock 的區別與用法
一、synchronized和lock的用法區別
synchronized:在需要同步的對象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括弧中表示需要鎖的對象。
lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做為鎖,多個線程中必須要使用一個ReentrantLock類做為對象才能保證鎖的生效。且在加鎖和解鎖處需要通過lock()和unlock()顯示指出。所以一般會在finally塊中寫unlock()以防死鎖。
二、synchronized和lock性能區別
synchronized是託管給JVM執行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因為這是一個重量級操作,需要調用操作介面,導致有可能加鎖消耗的系統時間比加鎖以外的操作還多。相比之下使用Java提供的Lock對象,性能更高一些。但是到了Java1.6,發生了變化。synchronize在語義上很清晰,可以進行很多優化,有適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。導致在Java1.6上synchronize的性能並不比Lock差。官方也表示,他們也更支持synchronize,在未來的版本中還有優化餘地。synchronized原始採用的是CPU悲觀鎖機制,即線程獲得的是獨占鎖。獨占鎖意味著其他線程只能依靠阻塞來等待線程釋放鎖。而在CPU轉換線程阻塞時會引起線程上下文切換,當有很多線程競爭鎖的時候,會引起CPU頻繁的上下文切換導致效率很低。而Lock用的是樂觀鎖方式。所謂樂觀鎖就是,每次不加鎖而是假設沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。樂觀鎖實現的機制就是CAS操作(Compare and Swap)。可以進一步研究ReentrantLock的源代碼,會發現其中比較重要的獲得鎖的一個方法是compareAndSetState。這里其實就是調用的CPU提供的特殊指令。現代的CPU提供了指令,可以自動更新共享數據,而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定。這個演算法稱作非阻塞演算法,意思是一個線程的失敗或者掛起不應該影響其他線程的失敗或掛起的演算法。
5. java多用戶同時修改一條數據時樂觀鎖怎麼用的
你說的這個version是mysql底層的鎖機制提供的,並不是java提供的。
使用數據版本(Version)記錄機制實現,這是mysql樂觀鎖最常用的一種實現方式。所謂的數據版本就是給數據增加一個版本標識,一般是通過為資料庫表增加一個數字類型的 「version」 欄位來實現。當讀取數據時,將version欄位的值一同讀出,數據每更新一次,對此version值加1。當我們提交更新的時候,判斷資料庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,如果資料庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期數據,版本號重新讀取再做更新。
6. java中悲觀鎖和樂觀鎖的區別
樂觀鎖和悲觀鎖的區別如下:
1、悲觀鎖是當線程拿到資源時,就對資源上鎖,並在提交後,才釋放鎖資源,其他線程才能使用資源。
2、樂觀鎖是當線程拿到資源時,上樂觀鎖,在提交之前,其他的鎖也可以操作這個資源,當有沖突的時候,並發機制會保留前一個提交,打回後一個提交,讓後一個線程重新獲取資源後,再操作,然後提交。和git上傳代碼一樣,兩個線程都不是直接獲取資源本身,而是先獲取資源的兩個版本,然後在這兩個版本上修改。
3、悲觀鎖和樂觀鎖在並發量低的時候,性能差不多,但是在並發量高的時候,樂觀鎖的性能遠遠優於悲觀鎖。
4、常用的synchronized是悲觀鎖,lock是樂觀鎖。
7. 深入研究 Java Synchronize 和 Lock 的區別與用法
在分布式開發中,鎖是線程式控制制的重要途徑。Java為此也提供了2種鎖機制,synchronized和lock。做為Java愛好者,自然少不了對比一下這2種機制,也能從中學到些分布式開發需要注意的地方。
我們先從最簡單的入手,逐步分析這2種的區別。
一、synchronized和lock的用法區別
synchronized:在需要同步的對象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括弧中表示需要鎖的對象。
lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做為鎖,多個線程中必須要使用一個ReentrantLock類做為對象才能保證鎖的生效。且在加鎖和解鎖處需要通過lock()和unlock()顯示指出。所以一般會在finally塊中寫unlock()以防死鎖。
用法區別比較簡單,這里不贅述了,如果不懂的可以看看Java基本語法。
二、synchronized和lock性能區別
synchronized是託管給JVM執行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因為這是一個重量級操作,需要調用操作介面,導致有可能加鎖消耗的系統時間比加鎖以外的操作還多。相比之下使用Java提供的Lock對象,性能更高一些。但是到了Java1.6,發生了變化。synchronize在語義上很清晰,可以進行很多優化,有適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。導致在Java1.6上synchronize的性能並不比Lock差。官方也表示,他們也更支持synchronize,在未來的版本中還有優化餘地。
說到這里,還是想提一下這2中機制的具體區別。據我所知,synchronized原始採用的是CPU悲觀鎖機制,即線程獲得的是獨占鎖。獨占鎖意味著其他線程只能依靠阻塞來等待線程釋放鎖。而在CPU轉換線程阻塞時會引起線程上下文切換,當有很多線程競爭鎖的時候,會引起CPU頻繁的上下文切換導致效率很低。
而Lock用的是樂觀鎖方式。所謂樂觀鎖就是,每次不加鎖而是假設沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。樂觀鎖實現的機制就是CAS操作(Compare and Swap)。我們可以進一步研究ReentrantLock的源代碼,會發現其中比較重要的獲得鎖的一個方法是compareAndSetState。這里其實就是調用的CPU提供的特殊指令。
現代的CPU提供了指令,可以自動更新共享數據,而且能夠檢測到其他線程的干擾,而 compareAndSet() 就用這些代替了鎖定。這個演算法稱作非阻塞演算法,意思是一個線程的失敗或者掛起不應該影響其他線程的失敗或掛起的演算法。
我也只是了解到這一步,具體到CPU的演算法如果感興趣的讀者還可以在查閱下,如果有更好的解釋也可以給我留言,我也學習下。
三、synchronized和lock用途區別
synchronized原語和ReentrantLock在一般情況下沒有什麼區別,但是在非常復雜的同步應用中,請考慮使用ReentrantLock,特別是遇到下面2種需求的時候。
1.某個線程在等待一個鎖的控制權的這段時間需要中斷
2.需要分開處理一些wait-notify,ReentrantLock裡面的Condition應用,能夠控制notify哪個線程
3.具有公平鎖功能,每個到來的線程都將排隊等候
下面細細道來……
先說第一種情況,ReentrantLock的lock機制有2種,忽略中斷鎖和響應中斷鎖,這給我們帶來了很大的靈活性。比如:如果A、B2個線程去競爭鎖,A線程得到了鎖,B線程等待,但是A線程這個時候實在有太多事情要處理,就是一直不返回,B線程可能就會等不及了,想中斷自己,不再等待這個鎖了,轉而處理其他事情。這個時候ReentrantLock就提供了2種機制,第一,B線程中斷自己(或者別的線程中斷它),但是ReentrantLock不去響應,繼續讓B線程等待,你再怎麼中斷,我全當耳邊風(synchronized原語就是如此);第二,B線程中斷自己(或者別的線程中斷它),ReentrantLock處理了這個中斷,並且不再等待這個鎖的到來,完全放棄。(如果你沒有了解java的中斷機制,請參考下相關資料,再回頭看這篇文章,80%的人根本沒有真正理解什麼是java的中斷,呵呵)
這里來做個試驗,首先搞一個Buffer類,它有讀操作和寫操作,為了不讀到臟數據,寫和讀都需要加鎖,我們先用synchronized原語來加鎖,如下:
1 public class Buffer {
2
3 private Object lock;
4
5 public Buffer() {
6 lock = this;
7 }
8
9 public void write() {
10 synchronized (lock) {
11 long startTime = System.currentTimeMillis();
12 System.out.println("開始往這個buff寫入數據…");
13 for (;;)// 模擬要處理很長時間
14 {
15 if (System.currentTimeMillis()
16 - startTime > Integer.MAX_VALUE)
17 break;
18 }
19 System.out.println("終於寫完了");
20 }
21 }
22
23 public void read() {
24 synchronized (lock) {
25 System.out.println("從這個buff讀數據");
26 }
27 }
28
8. 樂觀鎖的實現
Hibernate 在其數據訪問引擎中內置了樂觀鎖實現。如果不用考慮外部系統對資料庫的更新操作,利用 Hibernate 提供的透明化樂觀鎖實現,將大大提升我們的生產力。
Hibernate 中可以通過 class 描述符的 optimistic-lock 屬性結合 version
描述符指定。
現在,我們為之前示例中的 TUser 加上樂觀鎖機制。 首先為 TUser 的 class 描述符添加 optimistic-lock 屬性:
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
optimistic-lock="version"
>
……
</class>
</hibernate-mapping>
optimistic-lock 屬性有如下可選取值:
Ø none
無樂觀鎖
Ø version
通過版本機制實現樂觀鎖
Ø dirty
通過檢查發生變動過的屬性實現樂觀鎖
Ø all
通過檢查所有屬性實現樂觀鎖
其中通過 version 實現的樂觀鎖機制是 Hibernate 官方推薦的樂觀鎖實現,同時也
是 Hibernate 中,目前唯一在數據對象脫離 Session 發生修改的情況下依然有效的鎖機
制。因此,一般情況下,我們都選擇 version 方式作為 Hibernate 樂觀鎖實現機制。 添加一個 Version 屬性描述符
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
optimistic-lock="version"
>
<id
name="id"
column="id"
type="java.lang.Integer"
>
<generator class="native">
</generator>
</id>
<version
column="version"
name="version"
type="java.lang.Integer"
/>
……
</class>
</hibernate-mapping>
注意 version 節點必須出現在 ID 節點之後。
這里我們聲明了一個 version 屬性,用於存放用戶的版本信息,保存在 TUser 表的version 欄位中。
此時如果我們嘗試編寫一段代碼,更新 TUser 表中記錄數據,如:
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
List userList = criteria.list();
TUser user =(TUser)userList.get(0);
Transaction tx = session.beginTransaction();
user.setUserType(1); // 更新 UserType 欄位
tx.commit();
每次對 TUser 進行更新的時候,我們可以發現,資料庫中的 version 都在遞增。而如果我們嘗試在 tx.commit 之前,啟動另外一個 Session ,對名為 Erica 的用戶進行操作,以模擬並發更新時的情形:
Session session= getSession();
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
Session session2 = getSession();
Criteria criteria2 = session2.createCriteria(TUser.class);
criteria2.add(Expression.eq("name","Erica"));
List userList = criteria.list();
List userList2 = criteria2.list();TUser user =(TUser)userList.get(0);
TUser user2 =(TUser)userList2.get(0);
Transaction tx = session.beginTransaction();
Transaction tx2 = session2.beginTransaction();
user2.setUserType(99);
tx2.commit();
user.setUserType(1);
tx.commit();
執行以上代碼,代碼將在 tx.commit() 處拋出 StaleObjectStateException 異常,並指出版本檢查失敗,當前事務正在試圖提交一個過期數據。通過捕捉這個異常,我們就可以在樂觀鎖校驗失敗時進行相應處理。
9. java應用中怎麼實現訂單的鎖定效率比較高
java應用中怎麼實現訂單的鎖定效率比較高?
樂觀鎖在這種情況下不適用
訂單需要在編輯之前進行鎖定
1、單獨用表實現
2、用map實現
3、訂單表加一個鎖定欄位