Java 和线程安全

Java and thread safety

我正在创建一个多线程应用程序,我对同步方法的使用有疑问。

假设我有以下组件将被多个线程访问。

Component.java

public class Component {
    private boolean active;

    //Constructor
    public Component(){
        active = false;
    }

    synchronized public void initiate(){
        //do something
        active = true;
    }

    synchronized public void closedown(){
        //do something
        active = false;
    }

    public void doSomething(){
        //do something
    }

    public boolean isActive(){
        return active;
    }
}

如果我有两个线程访问同一个 Component 对象,并且第一个线程在设置 active = false 之前在 Component.closedown() 中停止,第二个线程启动并调用 Component.isActive(),第二个线程会阻塞直到第一个线程完成关闭,还是会得到返回值true?

如果是后者,如何让这个线程安全?

是的,这就是互斥锁(mutexes)的本质。如果线程在持有互斥锁时被 OS 取消调度,则所有其他需要互斥锁才能继续的线程都将停止。

以上就是为什么即使我们注意使所有关键部分都非常短且执行速度非常快,互斥锁仍会偶尔导致延迟尖峰的原因,并且尖峰与常规延迟成比例很大.例如,您的简单 getter 在无竞争时将在几纳秒内执行,但如果持有互斥锁的线程在不方便的时间取消调度,则可能需要 10 微秒或更长时间。

注意:您问题中的代码在 isActive 上缺少 synchronized 指定,但我认为您的问题是关于如果它被同步会发生什么——因为代码有一个数据竞争没有它。具体来说:

will the second thread block until the first thread has finished the closedown, or will it get the returned value of true?

如果没有 synchronized,它不会执行任何操作:它不会阻塞,但不能保证永远 return true 值。您只能保证观察到初始值(这就是数据竞赛的意义所在)。

如果您正在寻找实用的建议来改进您的代码,那么不要同步 isActive 方法,而是使 active 标志 volatile。这是您的用例的标准做法。

您需要使用 lock 来保护临界区。由于有些方法从值读取而有些方法写入值,您可以尝试使用 ReadWriteLock.

public class Component {
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private boolean active;

    //Constructor
    public Component(){
        active = false;
    }

    public void initiate(){
        // non-critical section
        rwl.writeLock().lock();
        try {
            // critical section
            active = true;
        } finally {
            rwl.writeLock().unlock();
        }
    }

    public void closedown(){
        // non-critical section
        rwl.writeLock().lock();
        try {
            // critical section
            active = false;
        } finally {
            rwl.writeLock().unlock();
        }
    }

    public void doSomething(){
        // do something
    }

    public boolean isActive(){
        rwl.readLock().lock();
        boolean status = active;
        rwl.readLock().unlock();
        return status;
    }
}