15.9 Kotlin 的多线程

Kotlin中没有synchronized关键字。
Kotlin中没有volatile关键字。
Kotlin的Any类似于Java的Object,但是没有wait(),notify()和notifyAll() 方法。

那么并发如何在Kotlin中工作呢?放心,Kotlin 既然是站在 Java 的肩膀上,当然少不了对多线程编程的支持——Kotlin通过封装 Java 中的线程类,简化了我们的编码。同时我们也可以使用一些特定的注解, 直接使用 Java 中的同步关键字等。下面我们简单介绍一下使用Kotlin 进行多线程编程的相关内容。

15.9.1 创建线程

我们在 Java 中通常有两种方法在Java中创建线程:

  • 扩展Thread类
  • 或者实例化它并通过构造函数传递一个Runnable

因为我们可以很容易地在Kotlin中使用Java类,这两个方式都可以使用。

使用对象表达式创建

  1. object : Thread() {
  2. override fun run() {
  3. Thread.sleep(3000)
  4. println("A 使用 Thread 对象表达式: ${Thread.currentThread()}")
  5. }
  6. }.start()

此代码使用Kotlin的对象表达式创建一个匿名类并覆盖run()方法。

使用 Lambda 表达式

下面是如何将一个Runnable传递给一个新创建的Thread实例:

  1. Thread({
  2. Thread.sleep(2000)
  3. println("B 使用 Lambda 表达式: ${Thread.currentThread()}")
  4. }).start()

我们在这里看不到Runnable,在Kotlin中可以很方便的直接使用上面的Lambda表达式来表达。

还有更简单的方法吗? 且看下文解说。

使用 Kotlin 封装的 thread 函数

例如,我们写了下面一段线程的代码

  1. val t = Thread({
  2. Thread.sleep(2000)
  3. println("C 使用 Lambda 表达式:${Thread.currentThread()}")
  4. })
  5. t.isDaemon = false
  6. t.name = "CThread"
  7. t.priority = 3
  8. t.start()

后面的四行可以说是样板化的代码。在 Kotlin 中把这样的操作封装简化了。

  1. thread(start = true, isDaemon = false, name = "DThread", priority = 3) {
  2. Thread.sleep(1000)
  3. println("D 使用 Kotlin 封装的函数 thread(): ${Thread.currentThread()}")
  4. }

这样的代码显得更加精简整洁了。事实上,thread()函数就是对我们编程实践中经常用到的样板化的代码进行了抽象封装,它的实现如下:

  1. public fun thread(start: Boolean = true, isDaemon: Boolean = false, contextClassLoader: ClassLoader? = null, name: String? = null, priority: Int = -1, block: () -> Unit): Thread {
  2. val thread = object : Thread() {
  3. public override fun run() {
  4. block()
  5. }
  6. }
  7. if (isDaemon)
  8. thread.isDaemon = true
  9. if (priority > 0)
  10. thread.priority = priority
  11. if (name != null)
  12. thread.name = name
  13. if (contextClassLoader != null)
  14. thread.contextClassLoader = contextClassLoader
  15. if (start)
  16. thread.start()
  17. return thread
  18. }

这只是一个非常方便的包装函数,简单实用。从上面的例子我们可以看出,Kotlin 通过扩展 Java 的线程 API,简化了样板代码。

15.9.2 同步方法和块

synchronized不是Kotlin中的关键字,它替换为@Synchronized 注解。 Kotlin中的同步方法的声明将如下所示:

  1. @Synchronized fun appendFile(text: String, destFile: String) {
  2. val f = File(destFile)
  3. if (!f.exists()) {
  4. f.createNewFile()
  5. }
  6. f.appendText(text, Charset.defaultCharset())
  7. }

@Synchronized 注解与 Java中的 synchronized 具有相同的效果:它会将JVM方法标记为同步。 对于同步块,我们使用synchronized() 函数,它使用锁作为参数:

  1. fun appendFileSync(text: String, destFile: String) {
  2. val f = File(destFile)
  3. if (!f.exists()) {
  4. f.createNewFile()
  5. }
  6. synchronized(this){
  7. f.appendText(text, Charset.defaultCharset())
  8. }
  9. }

跟 Java 基本一样。

15.9.3 可变字段

同样的,Kotlin没有 volatile 关键字,但是有@Volatile注解。

  1. @Volatile private var running = false
  2. fun start() {
  3. running = true
  4. thread(start = true) {
  5. while (running) {
  6. println("Still running: ${Thread.currentThread()}")
  7. }
  8. }
  9. }
  10. fun stop() {
  11. running = false
  12. println("Stopped: ${Thread.currentThread()}")
  13. }

@Volatile会将JVM备份字段标记为volatile。

当然,在 Kotlin 中我们有更好用的协程并发库。在代码工程实践中,我们可以根据实际情况自由选择。