android多线程访问变量,多线程的三个特征

## 1、有序性

**程序执行的顺序按照代码中定义的先后顺序执行。**

涉及到了 Java 中的指令重排序问题,在程序运行过程中,编译器和处理器会对指定做重排序。但是 JMM (Java Memory Model)能够确保在不同的编译器和不同的处理器平台上,通过插入指定类型的 Memory Barrier 来禁止特定类型的编译器重排序和处理器重排序,为上层提供一致的内存可见性保证。

**指令重排序不会影响代码在单线程中的结果,但是多线程中并发执行的结果则是不可控的。**

比如线程 A:

```

context = getContext();

inited = true

```

线程 B:

```

while(!inited){

sleep();

}

doSomeThingWithContext(context);

```

如果是线程 A 发生了重排序,变成:

```

inited = true

context = getContext();

```

如果 inited 为 true ,这个时候 B 线程会跳出 while 循环,然后执行 `doSomeThingWithContext(context);`,但是 A 的 `context = getContext();`还没初始化完成,就会引起错误发生。

再比如:

```

语句 1:int a = 80;

语句 2:String name = ""

语句 3:a += 3;

语句 4:b = a*a;

```

如果保证了有序性,那么就严格按照 1、2、3、4执行代码。

如果没保证有序性,单线程中执行是没问题的,即使是按照 2、1、3、4执行,也是正确的结果,但是在单线程中绝对不会 按照 3、1、2、4等顺序执行的。

如果要防止重排序,需要使用volatile关键字,volatile关键字可以保证变量的操作是不会被重排序的,但是记住 volatile 只对基本数据类型有效,因为除了基本类型的之外的操作,基本都不是原子操作。

## 2、可见性

**当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。**

Java 的线程通信是通过共享内存的方式进行的,为了加快程序的执行速度,cpu 会把数据加载到 cpu 的缓存中去,操作完之后再刷新到内存中。

比如:

![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20190617182816.png)

实际上,线程操作的是自己的工作内存(CPU 缓存),不会直接操作主内存(指的是 RAM),如果 共享变量的初始值为 1;上面的线程 A 对 a 在自己的工作内存中做了 +1 操作,还没来得及刷新到主内存,这个时候 B 线程也对主内存中的 a 做了 +1 操作,然后线程 A 和 B 都把自己的工作内存中的 a 刷新到 主内存中,这个时候就不会是 a = 3;而是 a = 2,因为线程 A 和线程 B 操作的时候 a 都是 1;这就是线程可见性带来的问题。

## 3、原子性

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

> 一个很经典的例子就是银行账户转账问题:

比如从账户A向账户B转1000元,那么必然包括2个操作:

1. 从账户A减去1000元,

2. 往账户B加上1000元。

这2个操作必须要具备原子性才能保证不出现一些意外的问题。

我们操作数据也是如此,比如i++;其中就包括三步,

1. 读取 i 的值

2. i 的值加 1

3. 将加 1 以后的值赋值给 i

这行代码在java中是不具备原子性的,这三步任何一步发生了错误,就会导致最后结果的错误,则多线程运行肯定会出问题,所以也需要我们使用同步和lock这些东西来确保这个特性了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值