多線程同步中的門道(一)

 

多線程同步中的門道(一)html

 

  在涉及到多線程的開發時,線程同步的考慮是不可缺乏的,不然極可能會形成各類超出預料的錯誤結果。以本身的學習經從來說,對於剛開始接觸線程同步的人可能會感受很是簡單,在多線程操做可能會形成數據混亂的地方同步一下不就好了嘛,加個synchronized關鍵字,多簡單!但是隨着開發的深刻,會漸漸的發現僅僅是一個synchronized關鍵字也不是那麼簡單,裏面的門道和考慮到的狀況仍是很多。本系列就着按部就班的程序和你們探討一下synchronized關鍵字使用中的各類情形和會形成的各類意料以外和意料之中的結果,歡迎各位大神輕拍。java

轉載請註明本文地址:http://www.cnblogs.com/hellojava/p/3630395.html安全

  synchronized涉及到同步方法、同步代碼塊、同步類、同步對象、靜態方法等,本系列來挨個探討。多線程

  注:由於考慮到文章篇幅和爲了突出咱們要分析的關鍵代碼,因此下面程序有可能不會是最優寫法。ide

未做線程同步學習

  咱們先來看看,在多線程運行下,未做線程同步的程序。測試

  [測試程序1]spa

/** * Test case 1. * There is no thread synchronization. */
public class Test { public static void main(String[] args) { final TestCase test = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test.function(); } }; thread1.start(); thread2.start(); } } class TestCase { public void function() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }

  上面的測試程序很簡單,定義了一個測試用例類,類中有一個循環輸出5次」線程名+輸出次數」的方法。而後設置了兩個線程,啓動這兩個線程跑這個測試用例對象的方法,查看會有什麼樣的輸出結果。後面的測試程序基本都是在此程序上修改變化而出,用來測試不一樣狀況。線程

  運行程序,某次運行的結果可能以下:code

Thread-0 executed result: 0 Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-0 executed result: 1 Thread-1 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-0 executed result: 4

  從程序輸出結果能夠看出,Thread-0和Thread-1是無規則交叉輸出的,也就意味着在未做線程同步的狀況下,兩個線程同時執行着TestCase的function方法,這種是屬於線程不安全的。

同步方法

  咱們對上面的程序進行一下修改,加一個synchronized關鍵字用來同步方法。

  [測試程序2.1]

/** * Test case 2.1. synchronized method. */
public class Test { public static void main(String[] args) { final TestCase test = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test.function(); } }; thread1.start(); thread2.start(); } } class TestCase { public synchronized void function() {// add synchronized keyword.
        for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }

  運行程序,獲得的輸出結果老是以下:

Thread-0 executed result: 0 Thread-0 executed result: 1 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-0 executed result: 4 Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-1 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4

  從輸出結果能夠看出,同步了方法以後,兩個線程順序輸出,說明線程Thread-1進入function方法後,線程Thread-2在方法外等待,等Thread-1執行完後釋放鎖,Thread-2才進入方法執行。

  可是咱們對上面的代碼稍做修改,看看同步方法,對於不一樣的對象狀況下是否都有線程同步的效果。

  [測試程序2.2]

/** * Test case 2.2. synchronized method but different objects. */
public class Test { public static void main(String[] args) { // There are two objects.
        final TestCase test1 = new TestCase(); final TestCase test2 = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test1.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test2.function(); } }; thread1.start(); thread2.start(); } } class TestCase { public synchronized void function() {// add synchronized keyword.
        for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }

  執行程序,某次的運行結果以下:

Thread-0 executed result: 0 Thread-0 executed result: 1 Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-0 executed result: 4 Thread-1 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4

  從以上結果能夠看出,同步方法時,當一個線程進入一個對象的這個同步方法時,另外一個線程能夠進入這個類的別的對象的同一個同步方法。

同步方法小結

  在多線程中,同步方法時:

  • 同步方法,屬於對象鎖,只是對一個對象上鎖;
  • 一個線程進入這個對象的同步方法,其餘線程則進不去這個對象全部被同步的方法,能夠進入這個對象未被同步的其餘方法;
  • 一個線程進入這個對象的同步方法,其餘線程能夠進入同一個類的其餘對象的全部方法(包括被同步的方法)。
  • 同步方法只對單個對象有用。

對靜態方法的同步

  上面是對普通的方法進行同步,發現只能鎖對象。那麼此次咱們試着同步靜態方法看會有什麼結果,與上面的[測試程序2.2]來進行對比。

  [測試程序3.1]

/** * Test case 3.1. synchronized static method. */
public class Test { public static void main(String[] args) { // There are two objects.
        final TestCase test1 = new TestCase(); final TestCase test2 = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test1.function(); } }; Thread thread2 = new Thread() { @Override public void run() { test2.function(); } }; thread1.start(); thread2.start(); } } class TestCase { public synchronized static void function() {// add static keyword.
        for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }

  執行程序,運行結果老是以下:

Thread-0 executed result: 0 Thread-0 executed result: 1 Thread-0 executed result: 2 Thread-0 executed result: 3 Thread-0 executed result: 4 Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-1 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4

  從結果能夠看出,兩個線程順序執行。而上面的[測試程序2.2]兩個線程是交叉輸出的。爲何同步靜態方法會起到兩個對象也能同步的了呢?

  由於被static修飾的方法是靜態方法,也被稱爲類方法,代表這個方法是屬於類的。與普通方法的區別是,在類被加載進內存時,就分配了方法的入口地址,而普通方法要實例化對象後才分配方法的入口地址。

  因此對靜態方法進行同步,至關因而鎖住了類,當一個線程進入這個靜態方法時,其餘線程不管使用這個類的哪一個對象都沒法進入這個靜態方法,直到上一個線程執行完畢釋放鎖。

  一樣的當一個線程進入這個靜態方法時,其餘線程也進不去這個類的其餘被同步的靜態方法,由於只要是靜態方法都是屬於類的嘛。可是能夠進入其餘未同步的方法(包括靜態方法)。這些能夠本身來測試,就不上例子了。

  可是這種方式,與徹底的同步類又有些區別,咱們能夠繼續看下面的程序。

  [測試程序3.2]

/** * Test case 3.2. synchronized static method. */
public class Test { public static void main(String[] args) { // There are two objects.
        final TestCase test = new TestCase(); Thread thread1 = new Thread() { @Override public void run() { test.function1(); } }; Thread thread2 = new Thread() { @Override public void run() { test.function2(); } }; thread1.start(); thread2.start(); } } class TestCase { public synchronized static void function1() {// add static keyword.
        for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } public synchronized void function2() {// no static keyword.
        for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " executed result: " + i); } } }

  執行程序,某次的運行結果以下:

Thread-0 executed result: 0 Thread-1 executed result: 0 Thread-1 executed result: 1 Thread-1 executed result: 2 Thread-0 executed result: 1 Thread-0 executed result: 2 Thread-1 executed result: 3 Thread-1 executed result: 4 Thread-0 executed result: 3 Thread-0 executed result: 4

  從以上結果能夠看出,雖然靜態方法和普通方法都被同步,雖然是對同一個對象,可是兩個線程仍然交叉執行。說明當一個線程進入了類的靜態同步方法,其餘線程能夠進入這個類的非靜態的同步方法。

同步靜態方法小結

  在多線程中,同步靜態方法時:

  • 同步靜態方法時,至關於對類全部的類方法上鎖,但並非徹底的類同步;
  • 一個線程進入這個類的靜態同步方法時,其餘線程沒法進入這個類的其餘靜態同步方法;
  • 可是其餘線程能夠進入這個類的非靜態方法(不管是否同步
  • 至於未同步的方法,不管是不是靜態方法,都是想進就進啦。
相關文章
相關標籤/搜索