在 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 实现。
相关文章
Kotlin 中 Java String[] 的等价物
发布时间:2023/05/13 浏览次数:59 分类:Java
-
本文介绍了 Kotlin 中 Java String[] 的等价物。 我们将看到所有可能的方法来为 Kotlin 实现与 Java 中的 String[] 相同的结果。
将 Java 文件代码转换为 Kotlin
发布时间:2023/05/13 浏览次数:142 分类:Java
-
Kotlin 现在是一种官方的 Android 语言。 因此,您可能希望将 Java 文件更改为 Kotlin。 本文教您如何将 Java 转换为 Kotlin。
在 Kotlin 中使用 forEach
发布时间:2023/05/13 浏览次数:122 分类:Java
-
本文介绍 Kotlin 中 forEach 关键字的概念和使用。 我们将看到一些使用 Kotlin forEach 循环的示例来理解它。
在 Kotlin 中使用 reified 关键字
发布时间:2023/05/13 浏览次数:197 分类:Java
-
reified 关键字是在 Kotlin 中使用泛型时最常使用的编程概念。在本教程中,我们将学习如何使用两种方法解决此问题,包括将类型的类作为泛型函数的参数传递,以及将 reified 关键字与内联函数
Kotlin 中 ! 和 ? 运算符之间的区别
发布时间:2023/05/13 浏览次数:180 分类:Java
-
在 Kotlin 中,断言运算符 !! 和安全调用符 ? 使用 Null 安全。本文介绍空安全的概念。 我们也将通过如何! 和 ? 在 Kotlin 中有助于空安全。
Kotlin 中 is 运算符 与 as 运算符之间的区别
发布时间:2023/05/13 浏览次数:148 分类:Java
-
这篇文章讨论了 Kotlin 中 is 和 as 运算符之间的区别。 我们还将介绍如何以及何时使用每一个。
Android Studio 中的 Kotlin 打印到控制台
发布时间:2023/05/13 浏览次数:52 分类:Java
-
Kotlin 的 logcat 窗口可以实时显示输出,让开发人员能够高效地处理他们的代码。 今天,我们将完成将消息打印到 Kotlin 控制台的步骤。
在 Kotlin 中获取随机数
发布时间:2023/05/13 浏览次数:68 分类:Java
-
随机数是使用一种算法从一组数字中生成的,该算法可确保每个数字的生成概率相等。在Kotlin中使用IntRange的random()扩展函数
在 Kotlin 中使用内联函数
发布时间:2023/05/13 浏览次数:199 分类:Java
-
Kotlin 允许使用内联函数有效地处理更高级的函数。 在本文中,我们将介绍我们需要了解的有关 Kotlin 中的内联函数的所有信息。