这个java类线程安全吗?

s3r*_*ius 10 java concurrency multithreading synchronized

这不是我的功课,这是给大学学生的一项任务.出于个人兴趣,我对解决方案感兴趣.

任务是创建一个包含整数的类(Calc).这两个方法add和mul应该添加或乘以这个整数.

同时设置两个线程.一个线程应该调用c.add(3)十次,另一个应该调用c.mul(3)十次(当然在同一个Calc对象上).

Calc类应确保操作交替完成(add,mul,add,mul,add,mul,..).

我没有经常处理与并发相关的问题 - 更不用说Java了.我已经为Calc提出了以下实现:

class Calc{

    private int sum = 0;
    //Is volatile actually needed? Or is bool atomic by default? Or it's read operation, at least.
    private volatile bool b = true;

    public void add(int i){
        while(!b){}
        synchronized(this){
                sum += i;
            b = true;   
        }
    }

    public void mul(int i){
        while(b){}
        synchronized(this){
            sum *= i;
            b = false;  
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

我想知道我是否走在正确的轨道上.对于while(b)部分来说,肯定有一种更优雅的方式.我想听听你们这些人的想法.

PS:不得更改方法的签名.除此之外,我不受限制.

Jiv*_*ngs 11

尝试使用Lock界面:

class Calc {

    private int sum = 0;
    final Lock lock = new ReentrantLock();
    final Condition addition  = lock.newCondition(); 
    final Condition multiplication  = lock.newCondition(); 

    public void add(int i){

        lock.lock();
        try {
            if(sum != 0) {
                multiplication.await();
            }
            sum += i;
            addition.signal(); 

        } 
        finally {
           lock.unlock();
        }
    }

    public void mul(int i){
        lock.lock();
        try {
            addition.await();
            sum *= i;
            multiplication.signal(); 

        } finally {
           lock.unlock();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

锁定就像你的同步块一样.但是这些方法将等待.await()另一个线程是否保持调用lockuntil .signal().


JB *_*zet 10

你所做的是一个繁忙的循环:你正在运行一个只在变量发生变化时停止的循环.这是一个糟糕的技术,因为它使CPU非常繁忙,而不是简单地使线程等待,直到更改标志.

我会使用两个信号量:一个用于multiply,一个用于add.add必须addSemaphore在添加之前获得,并multiplySemaphore在完成时释放许可,反之亦然.

private Semaphore addSemaphore = new Semaphore(1);
private Semaphore multiplySemaphore = new Semaphore(0);

public void add(int i) {
    try {
        addSemaphore.acquire();
        sum += i;
        multiplySemaphore.release();
    }
    catch (InterrupedException e) {
        Thread.currentThread().interrupt();
    }
}

public void mul(int i) {
    try {
        multiplySemaphore.acquire();
        sum *= i;
        addSemaphore.release();
    }
    catch (InterrupedException e) {
        Thread.currentThread().interrupt();
    }
}
Run Code Online (Sandbox Code Playgroud)


NPE*_*NPE 5

正如其他人所说,volatile你的解决方案是必需的.此外,您的解决方案旋转等待,这可能会浪费相当多的CPU周期.也就是说,就有关的正确性而言,我看不出任何问题.

我个人会用一对信号量来实现它:

private final Semaphore semAdd = new Semaphore(1);
private final Semaphore semMul = new Semaphore(0);
private int sum = 0;

public void add(int i) throws InterruptedException {
    semAdd.acquire();
    sum += i;
    semMul.release();
}

public void mul(int i) throws InterruptedException {
    semMul.acquire();
    sum *= i;
    semAdd.release();
}
Run Code Online (Sandbox Code Playgroud)


rat*_*eak 3

需要 volatile,否则优化器可能会将循环优化为if(b)while(true){}

但你可以用waitand来做到这一点notify

public void add(int i){

    synchronized(this){
        while(!b){try{wait();}catch(InterruptedException e){}}//swallowing is not recommended log or reset the flag
            sum += i;
        b = true;   
        notify();
    }
}

public void mul(int i){
    synchronized(this){
        while(b){try{wait();}catch(InterruptedException e){}}
        sum *= i;
        b = false;  
        notify();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是在这种情况下(b 在同步块内检查)不需要 volatile