“你好,阿米戈!我们有一种万能药,可以治愈所有疾病。正如我们所看到的,不受控制的线程切换是一个问题。”
“为什么线程本身不能决定何时切换到下一个线程?完成它们需要执行的所有工作然后发出信号“我完成了!”?
“允许线程自行控制切换将是一个更大的问题。假设你编写的代码不好,线程会永不放弃 CPU。在过去,这就是它的工作方式。那真是一场噩梦。”
“好的。那有什么解决方法呢?”
"阻塞其他线程。这就是它的工作方式。”
显然,线程在尝试使用共享对象和/或资源时会互相干扰。就像在具有控制台输出的示例中所看到的那样:有一个控制台,所有线程都输出到该控制台。这太混乱了。
因此发明了一个特殊的对象:互斥锁。就像洗手间门上写着“无人/有人”的标志一样。它有两种状态:对象可用或被占用。这些状态也称为“锁定”和“解锁”。
当线程需要与其他线程共享的对象时,它将检查与该对象关联的互斥锁。如果互斥锁已解锁,则线程将锁定它(将其标记为“已占用”)并开始使用共享资源。线程完成业务后,互斥锁将被解锁(标记为“可用”)。
如果线程要使用该对象且互斥锁已锁定,则线程在等待时会进入休眠状态。当互斥锁最终被占用线程解锁时,我们的线程将立即锁定它并开始运行。洗手间门标志这个比喻非常恰当。
“如何使用互斥锁呢?我需要创建特殊对象吗?”
“比那简单多了。Java 的创建者将此互斥锁内置到 Object 类中。因此,你甚至不必创建它。它是每个对象的一部分。下面是它的工作原理:”
代码 | 说明 |
---|---|
|
swap 方法交换 name1 和 name2 变量的值。
如果同时从两个线程中调用它会发生什么? |
实际代码执行 | 第一个线程的代码 | 第二个线程的代码 |
---|---|---|
|
|
|
末行 |
---|
变量的值被交换两次,各自返回其原始位置。 |
注意关键字 synchronized。
“嗯,它是什么意思?”
“当线程进入标记为 synchronized 的代码块时,Java 机器立即锁定 synchronized 一词后面括号内指示的对象的互斥锁。在我们的线程离开之前,没有其他线程可以进入此代码块。一旦我们的线程离开标记为 synchronized 的代码块,互斥锁将立即自动解锁,并可被另一个线程获取。”
如果互斥锁被占用,我们的线程将保持静止并等待其释放。
“如此简单精妙。很漂亮的解决方法。”
“是的。但是你知道在这种情况下会发生什么吗?”
代码 | 说明 |
---|---|
|
swap 和 swap2 方法共享同一个互斥锁 (this) 对象。 |
如果一个线程调用 swap 方法,而另一个线程调用 swap2 方法会发生什么?
“由于互斥锁相同,因此第二个线程将不得不等待,直到第一个线程离开 synchronized 代码块为止。因此,同时访问不会有任何问题。”
“做得不错,阿米戈!这就是正确答案!”
现在我想指出的是,synchronized 不仅可以用于标记代码块,而且可以用于标记方法。如下所示:
代码 | 实际发生的情况 |
---|---|
|
|
GO TO FULL VERSION