在 Kotlin 中使用 synchronized() 和 withLock() 来锁定共享资源
使用多线程应用程序时,我们需要确保管理对共享资源的访问以避免数据不一致。 当我们想要控制对应用程序中共享资源的访问时,同步是我们通常使用的方法。
同步是使用锁实现的,确保在当前执行的线程完成运行之前没有线程访问共享资源。 线程在访问共享资源之前必须首先获得对象的锁,并在完成后释放它。
创建锁的方法有多种,在本教程中,我们将学习如何在 Kotlin 中创建锁来控制对共享资源的访问。
生成 Kotlin 项目
打开 IntelliJ 开发环境并选择 File > New > Project。 在打开的窗口中,输入项目名称 kotlin-withlock,在 Language 部分选择 Kotlin,然后选择 Intellij 作为 Build System。 最后,按创建按钮生成项目。
生成应用程序后,在 src/main/kotlin 文件夹下创建一个名为 Main.kt 的文件。 我们将使用此文件来实现本教程的所有代码示例。
线程干扰在行动
线程干扰发生在多线程应用程序中,无法控制共享资源的访问方式。 下面的代码显示了线程干扰是如何在多线程应用程序中发生的。
将以下代码复制并粘贴到 Main.kt 文件中。
class Calculator {
fun addIntArray() {
var initialValue = 0;
for (number in 1..5) {
println(
Thread.currentThread().name
+ " ---> " + initialValue + " + "
+ number + " = "
.plus(String.format("%d", (initialValue + number)))
);
initialValue += number;
Thread.sleep(500);
}
}
}
fun main() {
val calculator = Calculator();
Thread {
calculator.addIntArray();
}.start();
Thread {
calculator.addIntArray();
}.start();
}
在这段代码中,我们在名为 Calculator 的类中创建了一个名为 addIntArray()
的方法。 此方法是我们代码中的共享资源,并且由于我们将创建多个线程,因此我们添加了一个 println() 方法,该方法允许我们将当前正在执行的线程记录到控制台。
当调用 sleep() 方法时,当前正在执行的线程被挂起 500 毫秒,另一个线程开始运行。 这意味着线程同时访问我们的方法,这会导致数据不一致。
在 main()
方法中,我们创建了两个线程并调用它们的 start()
方法以开始执行我们创建的计算器对象的 addIntArray() 方法。
运行这段代码并注意线程是如何交错的,如下所示。
Thread-0 ---> 0 + 1 = 1
Thread-1 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-0 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 10 + 5 = 15
在 Kotlin 中使用 synchronized() 函数锁定共享资源
此示例使用我们在上一节中实现的 addIntArray()
方法来展示如何管理对其的访问。 注释掉前面示例中创建的 main() 方法,并将以下代码复制并粘贴到注释后的 Main.kt 文件中。
val calculator = Calculator();
fun sumUsingSynchronization(): Unit = synchronized(calculator) {
calculator.addIntArray();
}
fun main() {
Thread {
sumUsingSynchronization();
}.start();
Thread {
sumUsingSynchronization()
}.start();
}
本篇文章的主要目的是了解如何创建锁以确保线程对共享资源的独占访问。 默认情况下,对象具有线程用于实现同步的隐式监视器锁,并且此代码通过将计算器对象传递给 synchronized() 函数来使用隐式监视器锁。
synchronized()
函数由 sumUsingSynchronization()
函数调用,而 sumUsingSynchronization()
函数又调用 addIntArray() 方法。 在 main() 方法中,我们创建了两个线程并调用了它们的 start() 方法来开始执行。
请注意,由于我们的代码使用隐式监视器锁,因此两个线程之间没有干扰。 第一个线程独占使用 addIntArray()
方法,直到它在第二个线程启动之前完成执行。
synchronized()
函数不仅限于隐式监视器锁,还可以使用任何其他锁,例如 ReentrantLock,我们只需要将锁作为该方法的参数传递即可。 运行此代码并确保输出如下所示。
Thread-0 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-0 ---> 3 + 3 = 6
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 0 + 1 = 1
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-1 ---> 10 + 5 = 15
在 Kotlin 中使用 withLock() 函数锁定共享资源
注释掉在上一个示例中创建的 main() 方法,并将以下代码复制并粘贴到注释后的 Main.kt 文件中。
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
val reentrantLock: Lock = ReentrantLock();
fun sumUsingLock(): Unit = reentrantLock.withLock {
calculator.addIntArray();
}
fun main() {
Thread {
sumUsingLock();
}.start();
Thread {
sumUsingLock()
}.start();
}
在这段代码中,我们创建了一个名为 sumUsingLock()
的方法,该方法分配给 Lock 接口的 withLock()
扩展函数。 withLock()
扩展函数使用提供的锁实现来专门为创建的多个线程中的每一个执行代码块。
在这个例子中,我们使用 ReentrantLock 创建了一个自定义锁,它实现了 Lock 接口。 此锁由创建的线程使用,以独占方式执行 addIntArray()
方法。
运行此代码并确保输出如下所示。
Thread-0 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-0 ---> 3 + 3 = 6
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 0 + 1 = 1
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-1 ---> 10 + 5 = 15
总结
在本篇文章中,我们了解了如何通过介绍如何在这些资源上创建锁来管理对共享资源的访问。 我们介绍了干扰是如何发生的,锁是如何与 synchronized()
函数一起使用的,以及锁是如何与 withLock()
函数一起使用的。
请注意,这两种方法之间的区别在于,synchronized() 函数不限于任何锁实现,而 withLock()
函数仅限于 Lock 实现。
相关文章
如何在 Java 中延迟几秒钟的时间
发布时间:2023/12/17 浏览次数:217 分类:Java
-
本篇文章主要介绍如何在 Java 中制造程序延迟。本教程介绍了如何在 Java 中制造程序延时,并列举了一些示例代码来了解它。
如何在 Java 中把 Hashmap 转换为 JSON 对象
发布时间:2023/12/17 浏览次数:187 分类:Java
-
它描述了允许我们将哈希图转换为简单的 JSON 对象的方法。本文介绍了在 Java 中把 Hashmap 转换为 JSON 对象的方法。我们将看到关于创建一个 hashmap,然后将其转换为 JSON 对象的详细例子。
如何在 Java 中按值排序 Map
发布时间:2023/12/17 浏览次数:171 分类:Java
-
本文介绍了如何在 Java 中按值对 Map 进行排序。本教程介绍了如何在 Java 中按值对 Map
进行排序,并列出了一些示例代码来理解它。
如何在 Java 中打印 HashMap
发布时间:2023/12/17 浏览次数:192 分类:Java
-
本帖介绍了如何在 Java 中打印 HashMap。本教程介绍了如何在 Java 中打印 HashMap 元素,还列举了一些示例代码来理解这个主题。
在 Java 中更新 Hashmap 的值
发布时间:2023/12/17 浏览次数:146 分类:Java
-
本文介绍了如何在 Java 中更新 HashMap 中的一个值。本文介绍了如何在 Java 中使用 HashMap 类中包含的两个方法-put() 和 replace() 更新 HashMap 中的值。
Java 中的 hashmap 和 map 之间的区别
发布时间:2023/12/17 浏览次数:79 分类:Java
-
本文介绍了 Java 中的 hashmap 和 map 接口之间的区别。本教程介绍了 Java 中 Map 和 HashMap 之间的主要区别。在 Java 中,Map 是用于以键值对存储数据的接口,
在 Java 中获取用户主目录
发布时间:2023/12/17 浏览次数:218 分类:Java
-
这篇文章向你展示了如何在 Java 中获取用户主目录。本教程介绍了如何在 Java 中获取用户主目录,并列出了一些示例代码以指导你完成该主题。
Java 中 size 和 length 的区别
发布时间:2023/12/17 浏览次数:179 分类:Java
-
这篇文章教你如何知道 Java 中大小和长度之间的区别。本教程介绍了 Java 中大小和长度之间的区别。我们还列出了一些示例代码以帮助你理解该主题。
Java 中的互斥锁
发布时间:2023/12/17 浏览次数:111 分类:Java
-
了解有关 Java 中互斥锁的一切,在计算机科学领域,互斥或互斥被称为并发控制的属性。每台计算机都使用称为线程的最小程序指令序列。有一次,计算机在一个线程上工作。为了更好地理解,