我发现几乎所有高级同步抽象(如Semaphore,CountDownLatch,来自java.util.concurrent的Exchanger)和并发集合都使用Unsafe中的方法(如compareAndSwapInt方法)来定义临界区。同时我预计同步块或方法将用于此目的。 你能解释一下,不安全的方法(我的意思是只能用原子方式设置一个值的方法)比同步更有效吗?为什么会这样?
答案 0 :(得分:5)
如果您希望等待很长时间(例如毫秒),因为线程可能会处于睡眠状态并释放CPU以执行其他工作,则使用synchronised
会更有效。
如果您希望操作很快发生,则使用compareAndSwap
会更有效。这是因为它是一个简单的机器代码指令,只需10 ns。但是,如果资源非常满足,则该指令必须忙等待,如果它无法获得所需的值,它可能会占用CPU,直到它为止。
如果使用off heap memory,则可以控制共享数据的布局,避免错误共享(多个CPU正在更新相同的缓存行)。当您有多个值可能需要单独更新时,这一点很重要。例如用于环形缓冲区。
请注意,典型JVM(例如,热点)的内部实现通常会使用比较和交换硬件指令作为synchronized
实现的 part ,如果这样的指令是可用(例如,x86),而另一个常见的替代方案是LL/SC(例如,POWER,ARM)。快速路径使用比较和交换(或等效)尝试获取锁定的典型策略(如果它是空闲的,可能是短spin-loop,最后如果失败回到操作系统级别阻塞原语(例如,futex,Events)。细节远不止于此,包括biased locking等技术,并且最终取决于实现。
答案 1 :(得分:0)
以上答案不令人满意。方法是:不需要互斥锁(同步),因为只有一个操作可以完成工作(在互斥锁中要做的所有事情),并且只有这一个操作不能中断。但这只是成功的一半,因为在多核系统中,另一个CPU可以写入相同的内存位置。因此,compareAndSwap机器码指令不仅在高速缓存中进行读取和写入,而且还对真实内存进行读取和写入。这需要更多时间访问RAM。 CompareAndSwap机器代码操作将检查RAM内容是否与读取前的值相比有所更改,然后才存储新值。如果我有更多的时间,我在这里写一个例子。
有效地,compareAndSwap访问比随时进行锁定和解锁要快。但是,只有在访问中只需要更改一个确切的存储位置时,才可以使用它。如果通常应更改多个存储位置(应始终保持一致),则不能使用compareAndSwap,只能使用同步。在上面写的答案中,compareAndSwap通常用于实现同步操作。这是正确的,因为单任务同步(获取互斥锁)和终端同步(释放互斥锁)仅需要任务调度程序中的一条原子指令。因此,原子访问是所有访问的基础。但是在同步{....}之间,调度程序知道线程开关受到保护。
这种程序方法不仅对于Java,对于C / ++(也许还有其他语言-)都是有效的,它也很重要并且可以使用。