程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java同步代碼等同於斷面

Java同步代碼等同於斷面

編輯:關於JAVA

同步經常作為斷面被引用。斷面是指一次只能有一個線程執行它。多個線程同時執行同步代碼是有可能的。

這個誤解是因為很多程序員認為同步關鍵字鎖住了它所包圍的代碼。但是實際情況不是這樣的。同步加鎖的是對象,而不是代碼。因此,如果你的類中有一個同步方法,這個方法可以被兩個不同的線程同時執行,只要每個線程自己創建一個的該類的實例即可。

參考下面的代碼:

class Foo extends Thread
{
 private int val;
 public Foo(int v)
 {
  val = v;
 }
 public synchronized void printVal(int v)
 {
  while(true)
   System.out.println(v);
 }
 public void run()
 {
  printVal(val);
 }
}
class SyncTest
{
 public static void main(String args[])
 {
  Foo f1 = new Foo(1);
  f1.start();
  Foo f2 = new Foo(3);
  f2.start();
 }
}

運行SyncTest產生的輸出是1和3交叉的。如果printVal是斷面,你看到的輸出只能是1或者只能是3而不能是兩者同時出現。程序運行的結果證明兩個線程都在並發的執行printVal方法,即使該方法是同步的並且由於是一個無限循環而沒有終止。

要實現真正的斷面,你必須同步一個全局對象或者對類進行同步。下面的代碼給出了一個這樣的范例。

class Foo extends Thread
{
 private int val;
 public Foo(int v)
 {
  val = v;
 }
 public void printVal(int v)
 {
  synchronized(Foo.class) {
   while(true)
    System.out.println(v);
  }
 }
 public void run()
 {
  printVal(val);
 }
}

上面的類不再對個別的類實例同步而是對類進行同步。對於類Foo而言,它只有唯一的類定義,兩個線程在相同的鎖上同步,因此只有一個線程可以執行printVal方法。

這個代碼也可以通過對公共對象加鎖。例如給Foo添加一個靜態成員。兩個方法都可以同步這個對象而達到線程安全。

譯者注:

下面筆者給出一個參考實現,給出同步公共對象的兩種通常方法:

1、

class Foo extends Thread
{
 private int val;
 private static Object lock=new Object();
 public Foo(int v)
 {
  val = v;
 }
 public void printVal(int v)
 {
  synchronized(lock) {
   while(true)
    System.out.println(v);
  }
 }
 public void run()
 {
  printVal(val);
 }
}

上面的這個例子比原文給出的例子要好一些,因為原文中的加鎖是針對類定義的,一個類只能有一個類定義,而同步的一般原理是應該盡量減小同步的粒度以到達更好的性能。筆者給出的范例的同步粒度比原文的要小。

2、

class Foo extends Thread
{
 private String name;
 private String val;
 public Foo(String name,String v)
 {
  this.name=name;
  val = v;
 }
 public void printVal()
 {
  synchronized(val) {
   while(true) System.out.println(name+val);
  }
 }
 public void run()
 {
  printVal();
 }
}
public class SyncMethodTest
{
 public static void main(String args[])
 {
  Foo f1 = new Foo("Foo 1:","printVal");
  f1.start();
  Foo f2 = new Foo("Foo 2:","printVal");
  f2.start();
 }
}

上面這個代碼需要進行一些額外的說明,因為JVM有一種優化機制,因為String類型的對象是不可變的,因此當你使用""的形式引用字符串時,如果JVM發現內存已經有一個這樣的對象,那麼它就使用那個對象而不再生成一個新的String對象,這樣是為了減小內存的使用。

上面的main方法其實等同於:

public static void main(String args[])
{
 String value="printVal";
 Foo f1 = new Foo("Foo 1:",value);
 f1.start();
 Foo f2 = new Foo("Foo 2:",value);
 f2.start();
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved