溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java的synchronized怎么用

發(fā)布時間:2022-01-10 09:18:15 來源:億速云 閱讀:132 作者:iii 欄目:編程語言

這篇文章主要講解了“Java的synchronized怎么用”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java的synchronized怎么用”吧!

要弄清synchronized的用法,首先要知道它是用來解決什么問題的.既然synchronized是同步的意思,那么它當(dāng)然就是來解決不同步的問題的.下面就舉一個不同步的例子來演示可能出現(xiàn)的問題.

在這個例子當(dāng)中,我們會創(chuàng)建兩個線程類.一個叫TwoCounter,其工作是對兩個計數(shù)器變量同時進(jìn)行累加,從1開始,你馬上會想道,我們是要用它來實現(xiàn)一個同步.另一個對象叫Watcher,顧名思義,是用來做監(jiān)視工作的,它負(fù)責(zé)檢查TwoCounter線程中的兩個計數(shù)器的值是否相等,看起來這似乎是毫無意義的工作,因為既然是同步累加的,那么兩個計數(shù)器的值怎么可能不相等呢??

但,事實情況不是這樣的.我們先來看程序.在看這個程序之前,最好先翻翻Think in Java的14.2.1,我的程序?qū)嶋H上是根據(jù)該節(jié)中給出的例子簡化的,其中的主類改作了Sharing2

class TwoCounter extends Thread {
  private int count1 = 0, count2 = 0;
  private boolean started=false;
  public void start(){
  if (!started) file://防止多次對一個線程調(diào)用Start方法
  {
  started=true;
  super.start();
  }
  }
  public void run() {
  while (true) {
  count1++;
file://如果TwoCounter運行到這個時候,cpu時間片被分配給了Watcher,那么這個時候Watcher讀出來的兩個計數(shù)器的值當(dāng)然會不一樣了,這個可能性是存在的?!斑@是由線程的本質(zhì)造成的——它們可在任何時候掛起(暫停)。所以在上述兩行的執(zhí)行時刻之間,有時會出現(xiàn)執(zhí)行暫?,F(xiàn)象。同時,Watcher線程也正好跟隨著進(jìn)來,并正好在這個時候進(jìn)行比較,造成計數(shù)器出現(xiàn)不相等的情況.”(Think in Java)
  count2++;
  System.out.println("Count1="+count1+",Count2="+count2);
  try {
  sleep(500);
  } catch (InterruptedException e){}
  }
  }

  public void synchTest() {
  Sharing2.incrementAccess();
  if(count1 != count2)
  System.out.println("Unsynched");//一旦發(fā)現(xiàn)不同步,立即顯示
  }
}

class Watcher extends Thread {
  private Sharing2 p;
  public Watcher(Sharing2 p) {
  this.p = p;
  start();
  }
  public void run() {
  while(true) {
  p.s.synchTest();
  try {
  sleep(500);
  } catch (InterruptedException e){}
  }
  }
}

public class Sharing2 {
  TwoCounter s;
  private static int accesSCOunt = 0;
  public static void incrementAccess() {
  accessCount++;
  System.out.println("accessCount="+accessCount);
  }
  public static void main(String[] args) {
  Sharing2 aaa = new Sharing2();
  aaa.s=new TwoCounter();
  aaa.s.start();//打開TwoCounter線程
  new Watcher(aaa);//打開Watcher線程
  }
}

上面的注釋講得很清楚了,有可能出現(xiàn)不同步的情況.但奇怪的是,我在運行的時候,卻始終沒有遇到不同步的情況,那么只有一種情況,就是程序中count1++和count2++幾乎是同時進(jìn)行的,watcher線程插不進(jìn)來,但是為什么Think in Java上面的程序運行之后就肯定有不同步的情況呢?兩個程序的原理是完全一樣的,唯一不同的是我的程序較為簡單,并且在命令行下運行,未使用GUI.難道是因為使用Applet方式運行或者以windows主窗口的方式運行開銷更大,使得watcher有機(jī)可趁嗎?于是我試著在count1++和count2++之間加了一條循環(huán)語句,人為的增大空隙,目的是為了讓watcher好插進(jìn)來,造成監(jiān)測出來的count1不等于count2的情況,實現(xiàn)不同步.修改后的程序是這樣的
  ......
  count1++;
  for(int i=0;i<5000;i++);
  count2++;
  ......

OK!再運行程序,很快就有不同步現(xiàn)象產(chǎn)生了,這似乎證明我剛才的分析是正確的.但奇怪的是,輸出了一次Unsynchrized之后,以后就再也沒有出現(xiàn)了,也就是說,watcher線程只有一次檢測到了兩個計數(shù)器count不同.這讓我覺得有點郁悶,是巧合還是必然呢?也許是時間太短了,等下去肯定還會有Unsynchrized輸出的.

算了,這個問題先放下來,我們繼續(xù).
既然出現(xiàn)了不同步的問題,那很顯然,解決的方法就是synchronized:將TwoCounter的run方法和SynchTest方法都變成同步方法.這樣做代表什么意思呢? 有什么好處呢?請參考Think in Java的14.2.2節(jié),里面有非常詳盡透徹的闡述.特別是對監(jiān)視器,也就是我們通常所說的對象鎖的概念,書中講的很清楚.

總之,需要修改的代碼如下:
class TwoCounter extends Thread {
  public synchronized void run() {
  while (true) {
  count1++;
  count2++;
  System.out.println("Count1="+count1+",Count2="+count2);
  try {
  sleep(500);
  } catch (InterruptedException e){}
  }
  }

  public synchronized void synchTest() {
  Sharing2.incrementAccess();
  if(count1 != count2)
  System.out.println("Unsynched");//一旦發(fā)現(xiàn)不同步,立即顯示
  }
}

略去其它不寫,表示從問題到解決其實很簡單,呵呵.
我們注意到無論run()還是synchTest()都是“同步的”。如果只同步其中的一個方法,那么另一個就可以自由忽視對象的鎖定,并可無礙地調(diào)用。所以必須記住一個重要的規(guī)則:對于訪問某個關(guān)鍵共享資源的所有方法,都必須把它們設(shè)為synchronized,否則就不能正常地工作。

現(xiàn)在又遇到了一個新問題。Watcher2永遠(yuǎn)都不能看到正在進(jìn)行的事情,因為整個run()方法已設(shè)為“同步”。而且由于肯定要為每個對象運行run(),所以鎖永遠(yuǎn)不能打開,而synchTest()永遠(yuǎn)不會得到調(diào)用。之所以能看到這一結(jié)果,是因為accessCount根本沒有變化。


為解決這個問題,我們能采取的一個辦法是只將run()中的一部分代碼隔離出來。想用這個辦法隔離出來的那部分代碼叫作“關(guān)鍵區(qū)域”,而且要用不同的方式來使用synchronized關(guān)鍵字,以設(shè)置一個關(guān)鍵區(qū)域。Java通過“同步塊”提供對關(guān)鍵區(qū)域的支持;這一次,我們用synchronized關(guān)鍵字指出對象的鎖用于對其中封閉的代碼進(jìn)行同步。如下所示:

synchronized(syncobject) {
  // This code can be accessed by only
  // one thread at a time, assuming all
  // threads respect syncObject's lock
}

在能進(jìn)入同步塊之前,必須在synchObject上取得鎖。如果已有其他線程取得了這把鎖,塊便不能進(jìn)入,必須等候那把鎖被釋放。
可從整個run()中刪除synchronized關(guān)鍵字,換成用一個同步塊包圍兩個關(guān)鍵行,從而完成對Sharing2例子的修改。但什么對象應(yīng)作為鎖來使用呢?那個對象已由synchTest()標(biāo)記出來了——也就是當(dāng)前對象(this)!所以修改過的run()方法象下面這個樣子:

file://注意沒有synchronized關(guān)鍵字了
  public void run() {
  while (true) {
  synchronized(this){
  count1++;
  count2++;
  }
  System.out.println("Count1="+count1+",Count2="+count2);
  try {
  sleep(500);
  } catch (InterruptedException e){}
  }
  }

file://注意,synchTest()還是要有synchronized關(guān)鍵字的,考慮一下為什么

這樣的話,synchTest方法就可以得到調(diào)用了,我們也可以看到accessCount的變化了.

感謝各位的閱讀,以上就是“Java的synchronized怎么用”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Java的synchronized怎么用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI