Java多线程与并发02: 线程同步

目录

多线程存在的问题:竞争条件1

多线程存在的问题:竞争条件2

多线程存在的问题:数据竞争

多线程存在的问题:缓存变量

使用同步代码

线程同步中可能存在的问题

Synchronize解决的问题

Volatile

final和volatile无法共存的问题

什么是immunable类


多线程存在的问题:竞争条件1

使用多线程可以提高效率,但是会带来其他问题,例如数据共享时出现的混乱问题。

我们考虑这样的代码(check + act)

在单线程程序中运行时,没有任何问题。a和b都是局部变量的时候,也都没有问题。(因为每个线程都会保存一份副本)

我们考虑这样的情形,a和b是成员变量或者静态成员变量,两个线程都来执行这段代码。

假设,线程1执行完判断语句,即将执行赋值语句,好了时间片结束了。这个时候,另一个线程把a的值给改了,当线程1恢复以后,可能不是5了。

 

 

多线程存在的问题:竞争条件2

还有可能是(read + modify + write)

假设两个线程同时执行这段代码。

线程1 读取初始值1,然后时间片结束。

线程2 读取初值1,自增,然后存储为2,返回1.

线程1 恢复,自增,然后存储2,返回1.

相当于线程1把线程2的操作给抹掉了(丢失更新)。

 

 

多线程存在的问题:数据竞争

我们来看一段初始化的代码

这里对parser进行了验证,如果为空,进行初始化。

当线程1读取为null,他来初始化,完成初始化后,线程2就获取到非空的数据。但是由于没有进行控制,线程1和线程2的写入和读取先后顺序没法保证,很有可能,线程1尚未完成初始化,线程2就读取完了,这样会导致parser初始化两次。

 

 

多线程存在的问题:缓存变量

jvm为了性能,对成员变量在各线程存副本,所以,对成员变量操作,很可能其他线程看不到。

这里线程t计算的result,很有可能主线程获取不到,因为result是一个静态成员变量,主线程中存有副本。

 

 

使用同步代码

使用synchronize锁定实例方法(使用的是ID的实例):

锁定静态方法(使用的是ID.class):

同步代码段:

 

 

线程同步中可能存在的问题

1. 死锁。

线程A等待B持有的资源,同时线程B等待A持有的资源。

两个方法,线程A调用方法1,线程B调用方法2,A线程获取了锁1,等待锁2,B线程获取了锁2,等待锁1,产生了死锁。

2. 活锁。

线程不断地重试一个失败的操作。

3. 饿死。

线程不断的等待资源,老是请求不到资源。例如高优先级的线程始终存在,低优先级的线程可能会饿死。

 

 

Synchronize解决的问题

解决的两方面问题:互斥和可见性。

 

 

Volatile

volatile解决的是可见性的问题,只解决可见性。

考虑下面的代码:

代码试图通过stopped标志控制线程的while循环,进而停止线程。但是结果不然。

这里涉及到变量缓存的问题,外部主线程更改的stopped状态,thd线程里没有觉察,因为里面还是stopped。

先来看一个错误的写法:

这里使用synchronize实现了同步,但是使用synchronize锁住了一个while循环!

我们使用synchronize是为了解决可见性的问题,也就是说,同步代码更新stopped的状态,但是,线程是先启动的,先获取了锁,然后一直在死循环里,为什么说是死循环?因为锁一致在,stopped始终是false,另一块stopThread方法获取不到锁,改不了stopped的状态。

为了给外部一个改变状态的机会,我们设置一个局部变量,先获取成员变量的副本,再执行打印:

嗯,这次解决了问题,但是,真的需要这样嘛?两个线程并没有并发的写入成员变量,而且,现在是一个线程写,一个线程读,所以同步代码是不必要的。

我们使用volatile来实现:

使用volatile标记的成员变量,各个线程不再保存副本,而是直接读写主内存。

所以调用起来没有问题,可以正常停止。


 

final和volatile无法共存的问题

使用final标记的不能使用volatile标记。

因为final字段已经保证了可见性,因为它是immunable的,所以无需使用volatile标记。

这个Planets类在构造器初始化Set以后,就不再允许修改内容了,也就是说planets已经属于不可影响的类了。

 

 

什么是immunable类

1. 不允许外界修改状态,也就是成员变量、类变量不允许set。

2. 所有的字段都是final

3. 实例的引用不会在构造器中转移。

下面的实例就导致了引用转移:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值