kotlin小悟-安全调用符
Posted 写代码的林克
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kotlin小悟-安全调用符相关的知识,希望对你有一定的参考价值。
今天看看kotlin中的安全调用符的一个注意点。
之前的文章已经讲过kotlin中的安全调用符,可以点击查看。
知识点
kotlin中的安全调用符 ?. 是线程安全的。
代码验证
我们打开IDEA写下面一段代码:
class Sample(var name: String?)
fun test()
if(name != null)
println(name.length)
然后IDEA会提示你如下一段话:
简单一点说,IDEA认为name属性可能会在你判断完成之后,在你正式使用该变量之前被修改,也就是不是线程安全的。
想想也是,假如有一个Sample对象,被两个线程读取和修改,在不加锁的情况下,即使你if判断是非空的,但是等你正式使用的时候,可能已经被其他线程修改为null,这时候就会得到不期望的结果。
由于存在编译错误,无法在kotlin中演示上面的问题,我们在Java里面通过下面的代码演示一下:
public class Demo
private String name;
public static void main(String[] args) throws InterruptedException
Demo demo = new Demo();
demo.name = "name";
Thread thread1 = new Thread(() -> demo.name = null);
Thread thread2 = new Thread(() ->
if (demo.name != null)
try
Thread.sleep(700);
System.out.println(demo.name.length());
catch (InterruptedException e)
e.printStackTrace();
else
System.out.println("deme.name is null");
);
thread2.start();
Thread.sleep(500);
thread1.start();
Thread.sleep(1000);
运行上面的代码,尽管已经通过了if判断,还是会抛出空指针异常,其实这就是简单的线程不安全的例子。
那kotlin的安全调用运算符是如何解决线程安全问题的呢?
我们可以尝试写一段最简单的代码,然后通过IDEA查看字节码,然后再从字节码反编译回Java代码进行查看。
我们先写下面一段代码:
class Sample(var name: String?)
fun info()
println("name len = " + name?.length)
我们通过点击工具栏的 Tools–>Kotlin–>Show Kotlin Bytecode 会在右侧打开一栏展示Kotlin的字节码,然后我们点击 Decompile 按钮就可以将字节码转为Java代码了,下面是转化后的Java代码:
import kotlin.Metadata;
import org.jetbrains.annotations.Nullable;
@Metadata(
mv = 1, 1, 15,
bv = 1, 0, 3,
k = 1,
d1 = "\\u0000\\u0016\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0000\\n\\u0002\\u0010\\u000e\\n\\u0002\\b\\u0005\\n\\u0002\\u0010\\u0002\\u0018\\u00002\\u00020\\u0001B\\u000f\\u0012\\b\\u0010\\u0002\\u001a\\u0004\\u0018\\u00010\\u0003¢\\u0006\\u0002\\u0010\\u0004J\\u0006\\u0010\\b\\u001a\\u00020\\tR\\u001c\\u0010\\u0002\\u001a\\u0004\\u0018\\u00010\\u0003X\\u0086\\u000e¢\\u0006\\u000e\\n\\u0000\\u001a\\u0004\\b\\u0005\\u0010\\u0006\\"\\u0004\\b\\u0007\\u0010\\u0004¨\\u0006\\u0000",
d2 = "LSample;", "", "name", "", "(Ljava/lang/String;)V", "getName", "()Ljava/lang/String;", "setName", "info", ""
)
public final class Sample
@Nullable
private String name;
public final void info()
StringBuilder var10000 = (new StringBuilder()).append("name len = ");
String var10001 = this.name;
String var1 = var10000.append(var10001 != null ? var10001.length() : null).toString();
boolean var2 = false;
System.out.println(var1);
@Nullable
public final String getName()
return this.name;
public final void setName(@Nullable String var1)
this.name = var1;
public Sample(@Nullable String name)
this.name = name;
其实,我们只需要关注上面的 info() 函数,因为这里面使用了安全调用符,其实我们只需要关注下面两行即可:
String var10001 = this.name;
String var1 = var10000.append(var10001 != null ? var10001.length() : null).toString();
kotlin将我们的成员变量赋值给了var10001这个变量,后续的判断和使用都是去使用这个变量。
这样即使你其他线程修改了name属性的值,也就是将name这个引用指向了其他变量,也不会影响到var10001这个变量,var10001是个局部变量,在每个线程中都有一份自己的存在,是不共享的,自然就是线程安全的了。
顺便说一句,你会发现kotlin中下面的代码是不会报错:
fun sample(str: String?)
if(str != null)
println(str.length)
就是因为局部变量是线程安全的,只有上面演示的成员变量使用if简单进行判断是线程不安全的。
写在最后
神秘的东西,揭开面纱以后,也就没那么神秘了。
以上是关于kotlin小悟-安全调用符的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin 符号( ‘?.‘ ‘?:‘ ‘!!‘ ‘as?‘ ‘?‘ )
Kotlin 符号( ‘?.‘ ‘?:‘ ‘!!‘ ‘as?‘ ‘?‘ )