前言

因为经常看到说,使用synchronized锁太重了。昨天尝试了一下使用Semaphore来代替锁实现多线程之间的同步。

发现如下信息:

当使用synchronized时,线程是 Blocked 的。
当使用Semaphore时,线程是 Waiting 的。

那么这两者之间有什么差异吗?

Difference between WAIT and BLOCKED thread states

The important difference between the blocked and wait states is the impact on the scheduler. A thread in a blocked state is contending for a lock; that thread still counts as something the scheduler needs to service, possibly getting factored into the scheduler's decisions about how much time to give running threads (so that it can give the threads blocking on the lock a chance).

阻塞状态(Blocked)和等待状态(Waiting)之间的重要区别是对调度器的影响。处于阻塞状态的线程正在竞争锁;该线程仍然算作调度程序需要服务的东西,可能会被纳入调度程序关于给正在运行的线程多少时间的决策中(这样它就能给在锁上阻塞的线程一个机会)。

Once a thread is in the wait state the stress it puts on the system is minimized, and the scheduler doesn't have to worry about it. It goes dormant until it receives a notification. Except for the fact that it keeps an OS thread occupied it is entirely out of play.

一旦一个线程处于等待状态,它对系统的压力就会最小化,调度程序不必担心它。它处于休眠状态,直到收到通知。除了让一个操作系统线程被占用之外,它完全没有发挥的余地。

This is why using notifyAll is less than ideal, it causes a bunch of threads that were previously happily dormant putting no load on the system to get woken up, where most of them will block until they can acquire the lock, find the condition they are waiting for is not true, and go back to waiting. It would be preferable to notify only those threads that have a chance of making progress.

这就是为什么使用notifyAll并不理想的原因,它导致了一堆之前愉快地休眠,没有给系统带来任何负担的线程被唤醒,它们中的大多数会阻塞,直到可以获得锁,发现它们所等待的条件不成立,然后继续等待。最好只通知那些有机会取得进展的线程。

(Using ReentrantLock instead of intrinsic locks allows you to have multiple conditions for one lock, so that you can make sure the notified thread is one that's waiting on a particular condition, avoiding the lost-notification bug in the case of a thread getting notified for something it can't act on.)

使用ReentrantLock锁而不是内在锁可以让你对一个锁有多个条件,这样你就可以确保被通知的线程是一个正在等待特定条件的线程,从而避免在一个线程得到它不能采取行动的通知的情况下出现丢失通知的错误。

Java 一点小问题

线程切到等待状态后(通常是 I/O)几乎不消耗 CPU 。用于 I/O (包括 HTTP) 的工作线程属于此类。

另一种情况是等一个锁,会进行自旋,不切到等待状态,略为消耗 CPU 。

但线程多了会消耗内存,还削弱了空间局部性,仍是一种负担。

单纯的自旋是一个 loop ,但一般的实现在 loop 几次后会考虑改变策略。 Java 的synchronized就是先自旋几次,不成功就退化为等待。

I/O 等待如你所想,之前等待的线程会被通知继续执行。

自旋是为了避免 context switch 的消耗吗?(可以这么说,而且有系统调用的开销) Thread.sleep()是不是也是自旋?(sleep 不是自旋)

CAS底层指令,会发生用户态和内核态的转换吗?

cas 指令不会切换核心态,lock 也不会切换,lock cas 也不会。

这个是普通指令,不是系统调用。

代码

代码详见:https://github.com/syuez/java_multithreading_design_pattern

另外观察到一个现象,基于上述代码的实现中,使用synchronized比使用Semaphore占用的堆内存更少。


参考资料:
Difference between WAIT and BLOCKED thread states
Java 一点小问题
CAS底层指令,会发生用户态和内核态的转换吗?

标签: none

评论已关闭