kotlin并发性
新开始读G. Blake Meike写的"Android Concurrency",到目前为止我非常推荐这本伟大的书,
它包含了许多关于不同的Android并发机制如何工作的深刻见解,当您更喜欢一种实现方式而不是另一种实现方式时,如何获得最好的方法。
我决定学习书中的例子,并且重写这些例子。由于我非常地热爱kotlin,我觉得把这些例子用kotlin实现是个不错的主意。
在Android Concurrency书的第一章,作者使用java中最基本的并发语法,因此现在我开始使用kotlin书写这些代码例子,我非常惊奇的发现:
在kotlin中没有synchronized关键字
在kotlin中没有volatile关键字
kotlin中的Any和java中的Object相似,但是没有wait(), notify() 和 notifyAll() 三个方法
那么并发是如何在kotlin中工作的呢?这个问题已经在kotlin forum中被问到了。如下是kotlin项目leader Andrey Breslav的回答:
Kotlin故意没有构建语言的并发性。我们认为这应该由libraries来处理。
尽管kotlin没有把并发性内置在语音中,但是仍然提供了很多低语言的并发语法。现在,让我们来看看这些语法。
Creating Threads
在java中有两种方法创建一个线程:
1:扩展Thread类
2:实例化Thread类并且通过构造函数传入一个Runnable
因为你能够kotlin中简单的使用java 类,上述两种方法也可以很好的起作用。
下面展示如下子类化Thread:object : Thread() {
override fun run() {
println("running from Thread:${Thread.currentThread()}")
}
}.start()
这部分代码使用到了kotlin的Object 表达式创建匿名类,并且重写了run()方法。此处将会演示如何传入一个Runnable对象来创建Thread的实例:Thread({
println("running from lambda:${Thread.currentThread()}")
}).start()
在这你并没有看到Runnable对象,在kotlin中你能够很容易的使用lambda表达式。是否还有更好的方法呢?当然!下面将会演示如果使用kotlin风格实例化并且启动一个线程:thread(start=true) {
println("running from thread():${Thread.currentThread()}")
}
很简洁,不是么?我们正在使用thread()方法,它会神奇地隐藏所有的样板代码。事实上,下面将展示完成的thread()方法:public fun thread(start: Boolean = true, isDaemon: Boolean = false,
contextClassLoader: ClassLoader? = null, name: String? = null,
priority:Int = -1, block: () -> Unit) : Thread {
val thread = object: Thread() {
public override fun run() {
block()
}
}
if (isDaemon)
thread.isDaemon = true
if (priority > 0)
thread.priority = priority
if (name != null)
thread.name = name
if(contextClassLoader != null)
thread.contextClassLoader = contextClassLoader
if(start)
thread.start()
return thread
}
它只是一个非常方便的包装函数,使用起来很方便。
Synchronized Methods and Blocks
在kotlin中,synchronized不是一个关键字,使用@Synchronized注解。
在kotlin中一个synchronized方法的声明看起来如下所示:@Synchronized fun synchronizedMethod() {
println("inside a synchronized method:${Thread.currentThread()}")
}
这个注解和Java中的synchronized有同样的效果:它将把JVM方法标记为同步。对应同步代码块,你不得不使用synchronized()方法,这将会使用一个lock作为一个参数:fun methodWithSynchronizedBlock() {
println("outside of a synchronized block:${Thread.currentThread()}")
synchronized(this) {
println("inside a synchronized block:${Thread.currentThread()}")
}
}
代码的外观和行为与Java变量非常相似。
Volatile Fields
同样地,在kotlin中没有volatile关键字,但是有@Volatile注解,@Volatile private var running = false
fun start() {
running = true
thread(start = true) {
while(running) {
println("Still running:${Thread.currentThread()}")
}
}
}
fun stop() {
running = false
println("Stopped:${Thread.currentThread()}")
}
和@Synchronized是相似的,@Volatile将把JVM支持字段标记为volatile。
wait(), notify() and notifyAll()
在kotlin中,每一个类都是从Any继承过来的,但是Any并没有声明wait(),notify()和notifyAll()方法,这就意味着,你不能在kotlin类中调用这些方法。但是你仍然能够使用java.lang.Object的实例作为lock,并且调用相关的方法。下面将会展示一个使用Objec做为lock解决生产者和消费者的问题,private val lock = java.lang.Object()
fun produce() = synchronized(lock) {
while(items>=maxItems) {
lock.wait()
}
Thread.sleep(rand.nextInt(100).toLong())
items++
println("Produced, count is$items:${Thread.currentThread()}")
lock.notifyAll()
}
fun consume() = synchronized(lock) {
while(items<=0) {
lock.wait()
}
Thread.sleep(rand.nextInt(100).toLong())
items--
println("Consumed, count is$items:${Thread.currentThread()}")
lock.notifyAll()
}
Does it look hacky? Well, it is(译者注:不是很明白)
事实是如果在你的代码中使用如此low-level constructs,看起来正在做一些错误的事情了。如今无论是在java中还是kotlin中都有很多高级的并发机制满足每一个需求,在Stackoverflow上面有一个非常棒的回答,并且提供了一系列可用的工具来写并发代码。
文章中的代码已经发布到GitHub,请参考。
Conclusion
尽快在项目中不会被频繁地使用到,但是了解和理解基础的知识还是比较重要的。最终发现在kotlin和java还是有一些不同的地方,但是主要的机制是一样的。请记住,kotlin和java的交互时候非常棒的,所以如果kotlin 没有counterparts,你就可以依赖Java类。玩得开心!