密码锁 java接口_从synchronized和lock区别入手聊聊java锁机制
寫這篇文章之前,我去百度了一下啥叫鎖,百度百科上寫道:置于可啟閉的器物上,以鑰匙或暗碼開啟。確實我們一般理解的鎖就是門鎖,密碼鎖,但是在計算機科學中,鎖又是啥,說實話,這個問題我也思考了很久,也沒法很好的用一兩句話就讓人聽得明白,也不想有人看到我的文章,然后將我的結論當作答案,我覺得最好的答案還是在探索的過程中得到的,接下來我們就好好探索一番。
作為一名java程序員,最開始接觸到的鎖就是synchronized,書本上是這么寫的,老師也是這么說的,至于為啥叫鎖,可能也沒多少人真的去思考過。不知道有沒有同學和我一樣,經歷過只知道用synchronized,后來逐漸的了解ReentrantLock,讀寫鎖,然后又了解了aqs,后來通過百度google,看一些博客(這個我要吐槽一下,在學習過程中遇到過很多文章寫的有問題的,反而誤導了我),后面看了看synchronized的源碼,最后對比synchronized和ReentrantLock才加深了對鎖的一些認知(說實話,作為一個剛畢業3年的非科班出身碼農,我也不敢保證自己寫的就一定對,算是學習過程中的一些感悟吧),那接下來我就按照學習順序來逐漸展開。
先來一段簡單的synchronized使用代碼:
public static voidmain(String[] args) {
String s= newString();synchronized(s) {
TestJni jni= newTestJni();
jni.jniHello();
}
}
上面代碼做的事情很簡單,如下圖所示,有A B C D E多個線程同時來到synchronized包含的代碼塊,A先一步進來了,那么BCDE都得等,等我A執行完他們才能進來執行。
? ? ? ?
synchronized用起來確實很簡單,我們也可以放在方法上,但是其本質還是鎖的對象,這個我們后面分析源碼一看就知道了。
隨著開發時間越長,synchronized在有些復雜場景下(如需要可中斷,可控制時間搶鎖,需要多個等待隊列分別控制,讀寫鎖等場景的時候)無法滿足我們的需求,那么就要用到Lock,下面我們先介紹一下Lock的簡單使用:
Lock lock = newReentrantLock();
lock.lock();try{
System.out.println("線程:"+Thread.currentThread().getName()+ " 進來啦");
}finally{
lock.unlock();
}
上面是一種最簡單的使用,和synchronized作用是一樣的,不過加鎖之后必須要解鎖,且必須緊跟try - finally塊解鎖,使用起來稍微復雜一點,容易出錯。
我們再介紹一種可中斷的使用方式:
public static voidmain(String[] args) {
Thread thread= new Thread(() ->{try{
lock.lockInterruptibly();try{
testLock();
}finally{
lock.unlock();
}
}catch(Exception e){
}
});
thread.start();
thread.interrupt();
}public static voidtestLock(){
condition.signalAll();
System.out.println("線程:"+Thread.currentThread().getName()+ " 進來啦");
}
這種方式呢,在拿鎖被park住了,如果剛好這時候被打斷了,就會響應打斷退出搶鎖并拋出異常,至于捕獲到異常開發者怎么做,那就得根據業務來分別處理了。
而像可控制時間的其實就要稍微復雜一點,先看一下synchronized中的使用:
static TestHash s = newTestHash();public static voidmain(String[] args) {
Thread thread1= new Thread(()->{
testLock();
});
Thread thread2= new Thread(()->{synchronized(s) {try{
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e) {
}
s.notify();
testLock();
}
});
thread1.start();
thread2.start();
}public static voidtestLock(){synchronized(s) {
System.out.println("線程:"+Thread.currentThread().getName()+ " 進來啦");try{
s.wait();
System.out.println("線程:"+Thread.currentThread().getName()+ " 叫醒啦");
}catch(InterruptedException e) {
System.out.println("拋異常啦");
}
}
}
這個例子看著要比前面幾個復雜一點,首先thread1會進入testLock方法,并拿到鎖,thread2等了1秒叫醒thread1(這里就是簡單的wait/notify的使用),然后在拿到鎖的情況下,再次進入testLock方法并拿到鎖,由于沒人喚醒了,會一直卡在這里(這里證明了synchronized的可重入),結果我就不貼了,感興趣的可以拿著代碼去試。
而ReentrantLock的使用也差不多,就是提前用lock去new一個Condition:
static ReentrantLock lock = newReentrantLock();static Condition condition =lock.newCondition();public static voidmain(String[] args) {
Thread thread1= new Thread(()->{
testWaitSingal();
},"thread1");
Thread thread2= new Thread(()->{
lock.lock();try{
TimeUnit.SECONDS.sleep(1);
condition.signal();
testWaitSingal();
}catch(InterruptedException e) {
}finally{
lock.unlock();
}
},"thread2");
thread1.start();
thread2.start();
}public static voidtestWaitSingal(){
lock.lock();try{
System.out.println("線程:"+Thread.currentThread().getName()+ " 進來啦");
condition.await();
System.out.println("線程:"+Thread.currentThread().getName()+ " 叫醒啦");
}catch(InterruptedException e) {
System.out.println("拋異常啦");
}finally{
lock.unlock();
}
}
可以看到兩種用法基本上是一致的,也就是將synchronized換成了lock,wait換成await,notify換成singal,
總結:
基本上我們平時用到的synchronized關鍵字的用法也就這些,但lock鎖不一樣,它還支持如上述的中斷,更復雜的讀寫鎖,還可以在aqs的基礎上衍生出更多,如countDownLatch,cyclicBarrier等,可以支持我們做更多,但是不是lock就可以完全替代synchronized了呢,其實synchronized也有自己的優點,簡單,不易出錯,性能也不比lock差(有的書上寫道synchronized性能比lock好,但其實就算好也不會好太多,對于我們來說,基本上可以忽略),真要說選哪個,我的建議是優先選synchronized,如果有特殊業務特殊需求synchronized無法滿足,那當然是要用lock,不過,一定要記得釋放鎖哦。
本來打算結合reentrant和synchronized直接串起來講的,但是確實有點多,這一篇就當作是后面的引子吧。
總結
以上是生活随笔為你收集整理的密码锁 java接口_从synchronized和lock区别入手聊聊java锁机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 杨梅有虫能不能吃?专家科普:就是蛋白质
- 下一篇: 航班起飞时发生巨响!疑似零件掉落:南航回