不使用synchronized的参考类型的线程安全性

dei*_*rus 2 java concurrency

我有以下类,它应该是线程安全的:

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);}

}
Run Code Online (Sandbox Code Playgroud)

当然,这在getter中存在可见性问题,因为只有在解锁和锁定监视器之间才能保证之前发生的关系.

显而易见的解决方法是将synchronized关键字添加到getter:

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);}

}
Run Code Online (Sandbox Code Playgroud)

我不是这个人的粉丝,因为这种方式我正在调用一个持有锁的外来方法,这就是要求可能的死锁.也许情况并非如此,但即便如此,我也不认为它足够优雅(如果我错了,请纠正我).

现在我想知道以下是否是线程安全的:

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);}

}
Run Code Online (Sandbox Code Playgroud)

我不认为这是因为我没有重写易失性参考本身.

那么,什么是使ShouldBeMadeThreadSafe线程安全的最佳方法?

Mar*_*nik 5

您误解了不要将外国代码称为持锁的建议.Arrays.copyOf 不是外来代码:它是JDK精确识别,精确指定的方法.外来代码的情况将是这样的:

public synchronized double[] getK(Runnable r) { r.run(); return k; };
Run Code Online (Sandbox Code Playgroud)

在这里,您正在执行run一个完全未知的类的方法,它可能会调用您的其他方法之一,从而破坏不变量.

synchronized在所有方法上使用都非常好; 是的,volatile不会帮助你.