2013-08-02 53 views
2

我有以下的類,它應該是線程安全的:線程安全的引用類型,而無需使用同步

public class ShouldBeMadeThreadSafe { 

    private double[] k = {70.0,70.0}; 
    private double[] b = {10.0,10.0}; 
    private double[] m = {5.0,6.0}; 

    public synchronized void setKX(double kx) {k[0]=kx;} 
    public synchronized void setKY(double ky) {k[1]=ky;} 
    public synchronized void setBX(double bx) {b[0]=bx;} 
    public synchronized void setBY(double by) {b[1]=by;} 
    public synchronized void setMX(double mx) {m[0]=mx;} 
    public synchronized void setMY(double my) {m[1]=my;} 

    public double[] getK() {return Arrays.copyOf(k, k.length);} 
    public double[] getB() {return Arrays.copyOf(b, b.length);} 
    public double[] getM() {return Arrays.copyOf(m, m.length);} 

} 

這當然有在干將知名度的問題,因爲之前發生關係只在顯示器的解鎖和鎖定之間保證。

明顯的解決將是synchronized關鍵字添加到干將:

public class OkButIDontLikeDeadlocks { 

    private double[] k = {70.0,70.0}; 
    private double[] b = {10.0,10.0}; 
    private double[] m = {5.0,6.0}; 

    public synchronized void setKX(double kx) {k[0]=kx;} 
    public synchronized void setKY(double ky) {k[1]=ky;} 
    public synchronized void setBX(double bx) {b[0]=bx;} 
    public synchronized void setBY(double by) {b[1]=by;} 
    public synchronized void setMX(double mx) {m[0]=mx;} 
    public synchronized void setMY(double my) {m[1]=my;} 

    public synchronized double[] getK() {return Arrays.copyOf(k, k.length);} 
    public synchronized double[] getB() {return Arrays.copyOf(b, b.length);} 
    public synchronized double[] getM() {return Arrays.copyOf(m, m.length);} 

} 

我不是這一個迷,因爲這樣我調用了鎖外星人方法舉行,這正在尋求可能的僵局。也許這不是這種情況,但即使如此,我認爲它不夠優雅(如果我錯了,請糾正我)。

現在我在想,如果下面是線程安全的:

public class AmIThreadSafe { 

    private volatile double[] k = {70.0,70.0}; 
    private volatile double[] b = {10.0,10.0}; 
    private volatile double[] m = {5.0,6.0}; 

    public void setKX(double kx) {k[0]=kx;} 
    public void setKY(double ky) {k[1]=ky;} 
    public void setBX(double bx) {b[0]=bx;} 
    public void setBY(double by) {b[1]=by;} 
    public void setMX(double mx) {m[0]=mx;} 
    public void setMY(double my) {m[1]=my;} 

    public double[] getK() {return Arrays.copyOf(k, k.length);} 
    public double[] getB() {return Arrays.copyOf(b, b.length);} 
    public double[] getM() {return Arrays.copyOf(m, m.length);} 

} 

我不認爲這是因爲我沒有改寫揮發性引用本身。

那麼,使ShouldBeMadeThreadSafe線程安全的最好方法是什麼?

回答

5

你誤解了建議不要調用持有鎖的外星代碼。 Arrays.copyOf不是外來代碼:它是一個精確確定的,精確指定的JDK方法。外來碼的情況下會是這樣的:

public synchronized double[] getK(Runnable r) { r.run(); return k; }; 

這裏你執行一個完全未知的類,還不如說你的其他方法之一,破壞了不變的run方法。

在所有方法上使用​​是完全正確的;是的,volatile不會幫助你。  

+0

我接受了這個答案,因爲它側重於我提出這個問題的原因,這是對「外星代碼」的含義的誤解。 – deinocheirus

1

由於您的理由,您確定這不是線程安全的。

你的三個主要選項是:

  • synchronize getter和setter方法(如在您的文章)
  • 寫在制定者的volatile參考; setKY(double ky) {k[1]=ky; k = k;}
  • 使用AtomicLongArray,它給你類似CAS的數組元素訪問器,包括內存可見性。您可以使用Double.doubleToLongBits

我會去的第一個,說實話雙打轉換和從多頭。這是最簡單的,並且行動將會非常迅速,以至於同步不會成爲瓶頸。

+0

您能解釋一下CAS類訪問器是什麼嗎?我搜索了一下,但沒有回答。 – deinocheirus

+1

[比較和交換](http://en.wikipedia。org/wiki/Compare-and-swap) – chrylis

+0

@deinocheirus CAS可以讓你原子地表達「如果值是X,則將其設置爲Y;否則不要更改它,讓我知道更改是否成功。但除了那種你不需要的功能之外,它還具有與volatile一樣的內存可見性語義:請參見http://docs.oracle.com/javase/7/docs/api/java /util/concurrent/atomic/package-summary.html – yshavit

相關問題