自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

初學(xué)Java多線程:使用Synchronized關(guān)鍵字同步類方法

開發(fā) 后端
本文介紹使用Synchronized關(guān)鍵字同步類方法。要達成Java多線程的run方法同步,需要在void和public之間加上synchronized關(guān)鍵字。

要想解決“臟數(shù)據(jù)”的問題,最簡單的方法就是使用synchronized關(guān)鍵字來使run方法同步,代碼如下:

  1. public synchronized void run()  
  2. {  
  3.        

從上面的代碼可以看出,只要在void和public之間加上synchronized關(guān)鍵字,就可以使run方法同步,也就是說,對于同一個Java類的對象實例,run方法同時只能被一個線程調(diào)用,并當(dāng)前的run執(zhí)行完后,才能被其他的線程調(diào)用。即使當(dāng)前線程執(zhí)行到了run方法中的yield方法,也只是暫停了一下。由于其他線程無法執(zhí)行run方法,因此,最終還是會由當(dāng)前的線程來繼續(xù)執(zhí)行。先看看下面的代碼:

sychronized關(guān)鍵字只和一個對象實例綁定

  1. class Test  
  2. {  
  3.       public synchronized void method()  
  4.      {  
  5.             
  6.      }  
  7. }  
  8.    
  9. public class Sync implements Runnable  
  10. {  
  11.      private Test test;  
  12.      public void run()  
  13.      {  
  14.           test.method();  
  15.      }  
  16.      public Sync(Test test)  
  17.      {  
  18.          this.test = test;  
  19.      }  
  20.      public static void main(String[] args) throws Exception  
  21.      {  
  22.          Test test1 =  new Test();  
  23.          Test test2 =  new Test();  
  24.          Sync sync1 = new Sync(test1);  
  25.          Sync sync2 = new Sync(test2);  
  26.          new Thread(sync1).start();  
  27.          new Thread(sync2).start();   
  28.      }  
  29.  } 

在Test類中的method方法是同步的。但上面的代碼建立了兩個Test類的實例,因此,test1和test2的method方法是分別執(zhí)行的。要想讓method同步,必須在建立Sync類的實例時向它的構(gòu)造方法中傳入同一個Test類的實例,如下面的代碼所示:

Sync sync1 = new Sync(test1);     不僅可以使用synchronized來同步非靜態(tài)方法,也可以使用synchronized來同步靜態(tài)方法。如可以按如下方式來定義method方法:

  1. class Test   
  2. {  
  3.     public static synchronized void method() {   }  

建立Test類的對象實例如下:

  1. Test test = new Test(); 

對于靜態(tài)方法來說,只要加上了synchronized關(guān)鍵字,這個方法就是同步的,無論是使用test.method(),還是使用Test.method()來調(diào)用method方法,method都是同步的,并不存在非靜態(tài)方法的多個實例的問題。

在23種設(shè)計模式中的單件(Singleton)模式如果按傳統(tǒng)的方法設(shè)計,也是線程不安全的,下面的代碼是一個線程不安全的單件模式。

  1. package test;  
  2.  
  3. // 線程安全的Singleton模式  
  4. class Singleton  
  5. {  
  6.     private static Singleton sample;  
  7.  
  8.     private Singleton()  
  9.     {  
  10.     }  
  11.     public static Singleton getInstance()  
  12.     {  
  13.         if (sample == null)  
  14.         {  
  15.             Thread.yield(); // 為了放大Singleton模式的線程不安全性  
  16.             sample = new Singleton();  
  17.         }  
  18.         return sample;  
  19.     }  
  20. }  
  21. public class MyThread extends Thread  
  22. {  
  23.     public void run()  
  24.     {  
  25.         Singleton singleton = Singleton.getInstance();  
  26.         System.out.println(singleton.hashCode());  
  27.     }  
  28.     public static void main(String[] args)  
  29.     {  
  30.         Thread threads[] = new Thread[5];  
  31.         for (int i = 0; i < threads.length; i++)  
  32.             threads[i] = new MyThread();  
  33.         for (int i = 0; i < threads.length; i++)  
  34.             threads[i].start();  
  35.     }  
  36. }  

在上面的代碼調(diào)用yield方法是為了使單件模式的線程不安全性表現(xiàn)出來,如果將這行去掉,上面的實現(xiàn)仍然是線程不安全的,只是出現(xiàn)的可能性小得多。

程序的運行結(jié)果如下:

25358555
26399554
7051261
29855319
5383406

上面的運行結(jié)果可能在不同的運行環(huán)境上有所有同,但一般這五行輸出不會完全相同。從這個輸出結(jié)果可以看出,通過getInstance方法得到的對象實例是五個,而不是我們期望的一個。這是因為當(dāng)一個線程執(zhí)行了Thread.yield()后,就將CPU資源交給了另外一個線程。由于在線程之間切換時并未執(zhí)行到創(chuàng)建Singleton對象實例的語句,因此,這幾個線程都通過了if判斷,所以,就會產(chǎn)生了建立五個對象實例的情況(可能創(chuàng)建的是四個或三個對象實例,這取決于有多少個線程在創(chuàng)建Singleton對象之前通過了if判斷,每次運行時可能結(jié)果會不一樣)。

要想使上面的單件模式變成線程安全的,只要為getInstance加上synchronized關(guān)鍵字即可。代碼如下:

  1. public static synchronized Singleton getInstance() {   } 

當(dāng)然,還有更簡單的方法,就是在定義Singleton變量時就建立Singleton對象,代碼如下:

  1. private static final Singleton sample = new Singleton(); 

然后在getInstance方法中直接將sample返回即可。這種方式雖然簡單,但不知在getInstance方法中創(chuàng)建Singleton對象靈活。讀者可以根據(jù)具體的需求選擇使用不同的方法來實現(xiàn)單件模式。

在使用synchronized關(guān)鍵字時有以下四點需要注意:

1.  synchronized關(guān)鍵字不能繼承。

雖然可以使用synchronized來定義方法,但synchronized并不屬于方法定義的一部分,因此,synchronized關(guān)鍵字不能被繼承。如果在父類中的某個方法使用了synchronized關(guān)鍵字,而在子類中覆蓋了這個方法,在子類中的這個方法默認(rèn)情況下并不是同步的,而必須顯式地在子類的這個方法中加上synchronized關(guān)鍵字才可以。當(dāng)然,還可以在子類方法中調(diào)用父類中相應(yīng)的方法,這樣雖然子類中的方法不是同步的,但子類調(diào)用了父類的同步方法,因此,子類的方法也就相當(dāng)于同步了。這兩種方式的例子代碼如下:

在子類方法中加上synchronized關(guān)鍵字

  1. class Parent  
  2. {  
  3.     public synchronized void method() {   }  
  4. }  
  5. class Child extends Parent  
  6. {  
  7.     public synchronized void method() {   }  

在子類方法中調(diào)用父類的同步方法

  1. class Parent  
  2. {  
  3.     public synchronized void method() {   }  
  4. }  
  5. class Child extends Parent  
  6. {  
  7.     public void method() { super.method();   }  

2.  在定義接口方法時不能使用synchronized關(guān)鍵字。

3.  構(gòu)造方法不能使用synchronized關(guān)鍵字,但可以使用下節(jié)要討論的synchronized塊來進行同步。

4.  synchronized可以自由放置。

在前面的例子中使用都是將synchronized關(guān)鍵字放在方法的返回類型前面。但這并不是synchronized可放置***位置。在非靜態(tài)方法中,synchronized還可以放在方法定義的最前面,在靜態(tài)方法中,synchronized可以放在static的前面,代碼如下:

  1. public synchronized void method();  
  2. synchronized public void method();  
  3. public static synchronized void method();  
  4. public synchronized static void method();  
  5. synchronized public static void method(); 

但要注意,synchronized不能放在方法返回類型的后面,如下面的代碼是錯誤的:

  1. public void synchronized method();  
  2. public static void synchronized method(); 

synchronized關(guān)鍵字只能用來同步方法,不能用來同步類變量,如下面的代碼也是錯誤的。

  1. public synchronized int n = 0;  
  2. public static synchronized int n = 0

雖然使用synchronized關(guān)鍵字同步方法是最安全的同步方式,但大量使用synchronized關(guān)鍵字會造成不必要的資源消耗以及性能損失。雖然從表面上看synchronized鎖定的是一個方法,但實際上synchronized鎖定的是一個類。也就是說,如果在非靜態(tài)方法method1和method2定義時都使用了synchronized,在method1未執(zhí)行完之前,method2是不能執(zhí)行的。靜態(tài)方法和非靜態(tài)方法的情況類似。但靜態(tài)和非靜態(tài)方法不會互相影響??纯慈缦碌拇a:

  1. package test;  
  2.  
  3. public class MyThread1 extends Thread  
  4. {  
  5.     public String methodName;  
  6.  
  7.     public static void method(String s)  
  8.     {  
  9.         System.out.println(s);  
  10.         while (true)  
  11.             ;  
  12.     }  
  13.     public synchronized void method1()  
  14.     {  
  15.         method("非靜態(tài)的method1方法");  
  16.     }  
  17.     public synchronized void method2()  
  18.     {  
  19.         method("非靜態(tài)的method2方法");  
  20.     }  
  21.     public static synchronized void method3()  
  22.     {  
  23.         method("靜態(tài)的method3方法");  
  24.     }  
  25.     public static synchronized void method4()  
  26.     {  
  27.         method("靜態(tài)的method4方法");  
  28.     }  
  29.     public void run()  
  30.     {  
  31.         try 
  32.         {  
  33.             getClass().getMethod(methodName).invoke(this);  
  34.         }  
  35.         catch (Exception e)  
  36.         {  
  37.         }  
  38.     }  
  39.     public static void main(String[] args) throws Exception  
  40.     {  
  41.         MyThread1 myThread1 = new MyThread1();  
  42.         for (int i = 1; i <= 4; i++)  
  43.         {  
  44.             myThread1.methodName = "method" + String.valueOf(i);  
  45.             new Thread(myThread1).start();  
  46.             sleep(100);  
  47.         }  
  48.     }  
  49. }  

運行結(jié)果如下:

非靜態(tài)的method1方法
靜態(tài)的method3方法

從上面的運行結(jié)果可以看出,method2和method4在method1和method3未結(jié)束之前不能運行。因此,我們可以得出一個結(jié)論,如果在類中使用synchronized關(guān)鍵字來定義非靜態(tài)方法,那將影響這個中的所有使用synchronized關(guān)鍵字定義的非靜態(tài)方法。如果定義的是靜態(tài)方法,那么將影響類中所有使用synchronized關(guān)鍵字定義的靜態(tài)方法。這有點象數(shù)據(jù)表中的表鎖,當(dāng)修改一條記錄時,系統(tǒng)就將整個表都鎖住了,因此,大量使用這種同步方式會使程序的性能大幅度下降。

【編輯推薦】

  1. 初學(xué)Java多線程:從線程返回數(shù)據(jù)的兩種方法
  2. 初學(xué)Java多線程:向線程傳遞數(shù)據(jù)的三種方法
  3. 初學(xué)Java多線程:慎重使用volatile關(guān)鍵字
  4. 初學(xué)Java多線程:join方法的使用
  5. 初學(xué)Java多線程:線程的生命周期
責(zé)任編輯:yangsai 來源: 真的有外星人嗎
相關(guān)推薦

2009-06-29 18:32:52

Java多線程Synchronize

2009-06-29 18:14:23

Java多線程volatile關(guān)鍵字

2017-05-27 20:59:30

Java多線程synchronize

2009-06-29 18:44:28

Java多線程Synchronize同步變量

2019-12-20 15:19:41

Synchroinze線程安全

2009-08-12 13:37:01

Java synchr

2011-04-14 13:27:53

Synchronize多線程

2021-03-10 15:59:39

JavaSynchronize并發(fā)編程

2024-03-15 15:12:27

關(guān)鍵字底層代碼

2020-07-17 20:15:03

架構(gòu)JMMvolatile

2024-11-20 15:55:57

線程Java開發(fā)

2010-03-15 18:11:38

Java多線程

2009-06-29 18:08:51

Java多線程join方法

2022-01-26 00:03:00

關(guān)鍵字線程JVM

2023-12-08 18:01:25

Java關(guān)鍵字

2009-06-25 10:33:53

StaticJava類

2021-01-12 09:22:18

Synchronize線程開發(fā)技術(shù)

2021-08-15 08:11:54

AndroidSynchronize關(guān)鍵字

2011-03-09 14:36:44

synchronizevolatile

2009-06-29 17:54:10

Java多線程Thread類創(chuàng)建線程
點贊
收藏

51CTO技術(shù)棧公眾號