今天來講一講java中的同步鎖,本文不對基本概念進行說明并且假定讀者已經知道鎖的基本用途?
鎖的作用:在多線程訪問同一數據時提供對數據的保護,防止數據被破壞或者不一致
本文目的:幫助讀者清晰的認識java中的同步鎖的相關知識,并能在程序中合理的使用
強烈建議自己嘗試動手用同步鎖實現一個生產者消費者模型再繼續往下看
生產者消費者簡單模型代碼參考:
public class Main{ List list = new ArrayList(); public static void main(String[] args) throws Exception { Main t = new Main(); Consumer consumer = t.new Consumer(); PRoducer producer = t.new Producer(); consumer.start(); producer.start(); } class Consumer extends Thread{ @Override public void run() { while (true) { synchronized (list) { if (list.size() > 0) { //如果list中有元素則消費掉 for(int i = 0 ;i < list.size();i++) { list.remove(i); System.out.println("消費1個"); } }else{ //如果list中沒有元素則喚醒其他等待線程,當前線程進入等待 list.notifyAll(); list.wait(); } } } } } class Producer extends Thread{ @Override public void run() { while (true) { synchronized (list) { if (list.size() == 0) { //如果list中沒有元素則生產 list.add(1); System.out.println("生產1個"); }else{ //如果有則喚醒其他線程執行,當前線程進入等待模式 list.notifyAll(); list.wait(); } } } } }以上是生產者和消費者的簡單模型示例。
整個程序的思路就是啟動兩個線程,一個是資源的生產者Producer,一個是資源的消費者Consumer,資源的容器是一個list,代碼邏輯是如果生產者發現list中沒有資源,就新增一個,如果有的話線程則進入等待狀態,消費者恰好相反,如果有就消費,沒有就等待。
上面的程序是可以按照我們設想的思路進行的,控制臺會不斷的打印 生產1個,消費1個,生產1個,消費1個………. ,這里涉及到幾個知識點要注意
如果能夠清晰的理解上面每行代碼的作用,說明對同步鎖的用法和基本就掌握了,如果還不是特別清楚,下面我們來詳細的分析上面的代碼和涉及到的知識點
第一:要保證一個線程在操作list的時候其他線程不能對它進行操作,否則會出現數據不一致的情況,所以要對list進行加鎖處理,防止其他線程篡改
第二:關于 wait(),notify(),notifyAll()方法,簡單的說這幾個方法是讓當前線程等待和喚醒的方法,是Object類的方法,因為Object是所有類的父類,所以說所有的對象都有這個方法
wait() :讓當前線程去等待該對象的鎖,那究竟是誰的鎖呢?誰調用的就去獲取誰的鎖,比如說上面調用了 list.wait(), 這就表示讓當前線程去等待,要調用wait()方法是有要求的,要求是當前線程必須已經擁有了對象的鎖,才能調用wait()方法,這里有的讀者可能就有點暈了,既然要擁有鎖之后才能調用wait(), 那調用wait是要等待什么呢? 這就和下面的一個方法產生關聯了,調用wait(),就是等待另一個線程調用notify()方法。
notify() :這個方法和上面的wait是對應的,是用來喚醒等待該資源的線程,相當于說,哎,兄弟我要把鎖釋放了,你們拿去用吧!,然后其他某個的線程就獲取到了鎖去繼續干活兒了,還有一個方法叫notifyAll,作用好像是和notify一樣,從jdk的說明文檔中沒有清晰的看出區別,使用上效果好像是一樣的。
對象鎖、類鎖、synchronized同步方法,以及synchronized同步代碼塊究竟是什么?它們之間又之間是什么關系?
對象鎖和synchronzied方法,請看如下代碼
// java中每個對象都有一把同步鎖,obj1和obj2各有一把一把同步鎖Object obj1 = new Object();Object obj2 = new Object();synchronized就是去獲取對象的同步鎖的意思,請看如下代碼
public void test1(){ synchronized(obj1){ System.out.println("執行了...."); }}上面的代碼是要獲取obj1對象的鎖,如果obj1對象的鎖被其他的線程持有了,那程序就會一直停在synchronzied那里,直到獲取到obj1的鎖才往下執行
但我們經常看到的同步代碼是這樣
public class Main{ public synchronized void doInSyn(){ System.out.println("執行咯......."); }}線程在執行doInSyn()方法時首先需要獲取當前對象的鎖才能執行,其實和下面這種寫法的效果是一樣
public class Main{ public void doInSyn(){ //代表先去獲取this對象的同步鎖才能執行 synchronized(this){ System.out.println("執行咯......."); } }}相信到這里應該已經理解對象鎖了,但有時我們會看到如下的寫法
public static synchronized doInSync(){}這里的synchronzied還是去獲取當前對象的同步鎖再執行的意思嗎?當然,答案是否定的,所以下面我們要講的是類的鎖,雖然名稱和對象鎖不一樣但其實原理是一樣的
Java中靜態方法是屬于類的,所以靜態方法的鎖也是類的鎖
Java中靜態方法是屬于類的,下面我們通過代碼來解釋
public class ClassLockTest extends Thread { public static void main(String[] args) { ClassLockTest t1 = new ClassLockTest(); t1.setName("T_One"); ClassLockTest t2 = new ClassLockTest(); t2.setName("T_Two"); t1.start(); t2.start(); } @Override public void run() { try { print(this.getName()); } catch (InterruptedException e) { e.printStackTrace(); } } public static synchronized void print(String threadName){ int count = 5; while(count-- > 0){ Thread.sleep(100); System.out.println(threadName + " "+count); } }}結果:T_One 4T_One 3T_One 2T_One 1T_One 0T_Two 4T_Two 3T_Two 2T_Two 1T_Two 0我們可以看到雖然同時啟動了兩個線程去打印,但順序完全沒有亂,就是因為靜態同步方法 (static synchronized修飾)會先去獲取對象的鎖,然后再執行,如果沒有獲取到則會一直等待,直到其他線程執行結束釋放鎖
上面的方法等同于這種寫法
public static void print(String threadName) { synchronized(ClassLockTest.class){ int count = 5; while(count-- > 0){ Thread.sleep(100); System.out.println(threadName + " "+count); }}}新聞熱點
疑難解答