溫馨提示×

溫馨提示×

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

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

synchronized關(guān)鍵字的使用

發(fā)布時間:2020-08-07 13:57:10 來源:網(wǎng)絡(luò) 閱讀:365 作者:nineteens 欄目:編程語言

  synchronized關(guān)鍵字是java并發(fā)編程中常使用的同步鎖,用于鎖住方法或者代碼塊,鎖代碼塊時可以是synchronized(this){}、synchronized(Object){}、synchronized(類class){}。

  當鎖住的內(nèi)容執(zhí)行完或者在執(zhí)行過程中拋出異常,才會自動釋放鎖。如果想手動釋放鎖,需要調(diào)用鎖住的對象的wait()方法釋放掉鎖并且置于等待狀態(tài),切換到其他線程運行,而notify()方法只是喚醒一個調(diào)用了該對象wait()方法的其他線程,但不會釋放鎖,選擇順序也不由代碼控制,由虛擬機實現(xiàn)。因此,對象的wait()、notify()、notifyAll()方法只能是配合synchronized關(guān)鍵字使用的,來完成線程間的調(diào)度。

  其中鎖住方法等同于synchronized(this){方法的所有代碼作為代碼塊},如下:

  public synchronized void test() {

  ...

  }

  等同于

  public void test() {

  synchronized (this) {

  ...

  }

  }

  上面的例子鎖住的是該類的對象,如果鎖住的是個靜態(tài)的方法,我們知道靜態(tài)方法是屬于類的而不屬于對象的,所以,synchronized修飾的靜態(tài)方法鎖定的是這個類的所有對象,即就算是兩個實例對象,只要他們都是這個類的,那都會鎖住。

  public synchronized static void test() {

  ...

  }

  等同于

  public static void test() {

  synchronized (所在類.class) {

  ...

  }

  }

  無論是鎖方法還是鎖代碼塊,無論鎖代碼塊時的參考對象是什么,只要記住一個原則就一目了然了,那就是當參考對象相同時,同步鎖才起作用,否則鎖不會互斥,可以并發(fā)執(zhí)行。

  synchronized(this)表示當前類的對象實例相同時鎖起作用,synchronized(Object)表示該Object對象相同時鎖起作用,synchronized(類class)表示當都是該class類時鎖起作用。

  舉一個簡單的例子:

  public class TestController {

  public class Task implements Runnable{

  private String str;

  Task(String str){

  this.str=str;

  }

  @Override

  public void run() {

  synchronized (str) {

  try {

  Thread.sleep(3000l);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println(str);

  }

  }

  }

  public static void main(String[] args) throws InterruptedException {

  TestController testController = new TestController();

  Thread thread1 = new Thread(testController.new Task("1"));

  Thread thread2 = new Thread(testController.new Task("1"));

  thread1.start();

  thread2.start();

  }

  }

  上述代碼,參照對象str都是"1",在java中,String字符串如果通過this.str="1"這樣的方式賦值就等同于str=String.valueOf("1"),如果字符串"1"之前已經(jīng)初始化過,那就會直接拿之前的,所以是同一個對象。根據(jù)上面介紹的原則,那鎖就會起作用,所以結(jié)果是3秒之后輸出1,再過3秒再輸出1。

  如果把thread2改成

  Thread thread2 = new Thread(testController.new Task("2"));

  這時參考對象一個是"1",另一個是"2",不是同一個對象,所以鎖不會互斥,就不會起作用,所以結(jié)果是3秒后幾乎同時輸出1和2。

  以上都是多個線程同時調(diào)用同一個方法,如果調(diào)用不同的方法呢?

  public class Test{

  public synchronized void m1(){

  System.out.println("m1 running...");

  try {

  Thread.sleep(3000l);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println("m1 end");

  }

  public synchronized void m2(){

  System.out.println("m2 running...");

  System.out.println("m2 end");

  }

  public static void main(String[] args) {

  Test test = new Test();

  new Thread(new Runnable() {

  @Override

  public void run() {

  test.m1();

  }

  }).start();

  new Thread(new Runnable() {

  @Override

  public void run() {

  test.m2();

  }

  }).start();

  }

  }

  上面代碼的輸出結(jié)果是:

  m1 running...

  //過3秒

  m1 end

  m2 running...

  m2 end

  上面就說過synchronized修飾在方法上等同于synchronized(this){方法的所有代碼作為代碼塊},而this就代表是對象,也就是說,第一個Thread獲得的是test對象的鎖,因為對象都是同一個test,所以第二個Thread無法獲取到鎖,而被阻塞。

  把上面的例子改造成如下:

  private String str = "1";

  public void m1(){

  synchronized(str){

  System.out.println("m1 running...");

  try {

  Thread.sleep(3000l);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println("m1 end");

  }

  }

  public void m2(){

  synchronized(str){

  System.out.println("m2 running...");

  System.out.println("m2 end");

  }

  }無錫婦科醫(yī)院排名 http://www.csfk0731.com/

  第一個Thread調(diào)用m1()時獲取到的是對象str的鎖,第二個Thread調(diào)用m2()時也需要獲取對象str的鎖,而且因為是同一個Test對象,所以兩個str也是同一個對象,所以第二個Thread會因為獲取不到鎖而被阻塞,輸出結(jié)果和之前的例子一樣。

  如果再把上面的例子改造成如下:

  public class M1 {

  public void m(String str){

  synchronized (str) {

  System.out.println("m1 runing");

  try {

  Thread.sleep(3000l);

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println("m1 end");

  }

  }

  }

  public class M2 {

  public void m(String str){

  synchronized (str) {

  System.out.println("m2 runing");

  System.out.println("m2 end");

  }

  }

  }

  public class Test {

  public static void main(String[] args) {

  String str = "1";

  new Thread(new Runnable() {

  @Override

  public void run() {

  new M1().m(str);

  }

  }).start();

  new Thread(new Runnable() {

  @Override

  public void run() {

  new M2().m(str);

  }

  }).start();

  }

  }

  這次調(diào)用的方法在兩個類里面,但是結(jié)果和之前的兩個例子是一樣的,因為鎖住的都是傳進來的str對象,同一個對象只有一把鎖,第一個Thread拿了,第二個Thread就只能等待。

  總結(jié):

  A. 無論synchronized關(guān)鍵字加在方法上還是對象上,如果它作用的對象是非靜態(tài)的,則它取得的鎖是對象;如果synchronized作用的對象是一個靜態(tài)方法或一個類,則它取得的鎖是對類,該類所有的對象同一把鎖。

  B. 每個對象只有一個鎖(lock)與之相關(guān)聯(lián),誰拿到這個鎖誰就可以運行它所控制的那段代碼。

  C. 實現(xiàn)同步是要很大的系統(tǒng)開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。


向AI問一下細節(jié)

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

AI