Java培訓教程之使用Lock取代synchronized

千鋒武漢發表於2021-07-15

  在多執行緒中,1.5版本之前,我們都使用同步程式碼塊或者同步方法來解決執行緒安全問題

  比如:

  同步程式碼塊

  synchronized(鎖物件){

  功能程式碼;

  }

  同步方法

  public synchronized void test(){

  功能程式碼;

  }

  在1.5版本,在java的工具類包中,java.util.concurrent.locks 中,推出了最新的

  解決執行緒安全的方法,就是Lock+Condition的方式,

  那這兩種方式之間的關係和區別是什麼呢?

  加入對於執行緒常見的生產者消費者模式,我們使用兩種方式實現,來看看他們的區別:

  第一種,使用synchronized

  我們完成一個男人掙錢女人花的例子

  需求是如果沒錢,男人執行緒掙一次錢,等待,然後喚醒女人去花錢,花完錢,等待,喚醒

  男人去掙錢,一直迴圈這個過程,將男人執行緒看做生產執行緒,女人執行緒看做消費執行緒。

  程式碼如下:

  //首先完成共享資料

  class Person{

  String name;

  boolean isMoney=true;

  //掙錢

  public void zhengQian(){

  synchronized(this){

  while(!isMoney){

  try{wait();}catch(Exception e){}

  }

  name="男人";

  System.out.println(Thread.currentThread().getName()+name+"---掙錢---");

  isMoney=!isMoney;

  notifyAll();//叫醒所有

  }

  }

  //花錢

  public void huaQian(){

  synchronized(this){

  while(isMoney){

  try{wait();}catch(Exception e){}

  }

  name="women。。。。人";

  System.out.println(Thread.currentThread().getName()+name+"---花=========錢---");

  isMoney=!isMoney;

  notifyAll();//叫醒所有

  }

  }

  }

  //輸入執行緒

  class In implements Runnable{

  Person p=null;

  In(Person p){

  this.p=p;

  }

  public void run(){

  while(true){

  p.zhengQian();

  }

  }

  }

  //輸出執行緒

  class Out implements Runnable{

  Person p=null;

  Out(Person p){

  this.p=p;

  }

  public void run(){

  while(true){

  p.huaQian();

  }

  }

  }

  從程式碼中可以看出,對於同步程式碼塊或者同步方法來說,都是以鎖物件this來控制其鎖定的程式碼的。

  api中也告訴我們:

  Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支援多個相關的 Condition 物件。

  那使用Lock+Condition怎麼來實現呢?

  程式碼如下:

  import java.util.concurrent.locks.*;

  class Person{

  String name;

  boolean isMoney=true;

  Lock lock=new ReentrantLock();

  Condition in=lock.newCondition();

  Condition out=lock.newCondition();

  //掙錢

  public void zhengQian(){

  lock.lock();

  try{

  while(!isMoney){

  try{in.wait();}catch(Exception e){}

  }

  name="男人";

  System.out.println(Thread.currentThread().getName()+name+"---掙錢---");

  isMoney=!isMoney;

  out.signal();//叫醒對方

  }finally{

  lock.unlock();

  }

  }

  //花錢

  public void huaQian(){

  lock.lock();

  try{

  while(!isMoney){

  try{out.wait();}catch(Exception e){}

  }

  name="women。。。。人";

  System.out.println(Thread.currentThread().getName()+name+"---花=========錢---");

  isMoney=!isMoney;

  in.signal();//叫醒對方

  }finally{

  lock.unlock();

  }

  }

  }

  對比發現,Lock+Condition的方式是使用Condition物件來具體操作現場的等待和喚醒的,

  也就是對於生產執行緒有專屬的生成執行緒的Condition物件,對消費執行緒有專屬的消費執行緒的Condition

  物件,當等待或者喚醒時可以精確的控制是哪方執行緒,同步程式碼塊或者同步方法在喚醒時,不能精確的

  喚醒對方,所以只能喚醒全部,這時候增加了執行緒的判斷次數,明顯,Lock+Condition是優於synchronized的方式的

  接下來我們分析下Lock和Condition的關係

  Lock的子類物件是來取代synchronized的,也就是鎖,Condition只是鎖的操作物件

  取代的是以前的鎖物件,都擁有等待和喚醒的方法,雖然名字不同,Condition主要是對鎖的操作物件

  進行分類,如下圖:

1

  上圖中,一個Lock物件,建立出來三個Condition物件 x,y,z

  也就是以後執行這個鎖的執行緒將會被分成三類執行緒,比如如果執行x.await(),執行這段程式碼的

  三個綠色執行緒將會進入等待狀態,再比如z中三個紅色執行緒都處於等待狀態,如果執行z.signal(),將會喚醒執行z的三個紅色執行緒中的某一個,這樣程式碼就可以正常執行了。

  藍色線兩側分別表示兩個Lock的子類物件,每一個lock的Condition之間可以互相操作,對於兩個

  鎖之間的Condition,比如,x和Q,就沒有直接的關係了

  好了,這篇文章主要說明下Lock+Condition方式來取代synchronized,希望對大家有所幫助。

  本文來自千鋒教育,轉載請註明出處。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31548651/viewspace-2781403/,如需轉載,請註明出處,否則將追究法律責任。

相關文章