并发编程基础三

 

1.并行与并发区别

并发指同一时间段多个任务同时都在进行,并且都没有执行结束,而并行是说在单位时间内多个任务在同时运行。

并发任务强调在一个时间段内同时进行,而一个时间段有多个单位i时间构成,所以说并发的多个任务在单位时间内不一定同时在执行。

一个CPU同时只能执行一个任务,所以单CPU时代多个任务都是并发执行的。

注:在多线程时间中,线程的个数往往多于CPU个数,所以即使存在并行任务,一般还是称为多线程并发编程而非多线程并行编程。


2.Java中的线程安全问题

线程安全问题是指当多个线程同时读写一个共享资源并且没有任何同步措施时,导致出现脏数据或者其他不可预见的结果的问题


3.Java中共享变量的内存可见性问题

Java内存模型规定将所有的变量都存放在主内存中,当线程使用变量时,会把主内存里面的变量复制到自己的工作空间或者叫作工作内存,线程读写变量时操作的是自己工作内存中的变量。

当一线程操作共享变量时,它首先从主内存复制共享变量到自己的工作内存,然后工作内里的变量进行处,处理完后将变量值更新到主内存。那么假如线程A和线程B同时处理一个共享变量,会出现什么情况?我们使用上图所示CPU架构,假设线程A和线程B使用不同CPU执行,并且当前两级Cache都为空,那么这时候由于Cache的存在,将会导致内存不可见问题,具体看下面的分析。·线程A首先取共享变量X的值,由于两级Cache都没有命中,所以加载主内存中X的值,假如为0。然后把X=O的值缓存到两级缓存,线程A修改X的值为1,然后将其写入两级Cache,并且刷新到主内存。线程A操作完毕后,线程A所在的CPU的两级Cache内和主内存里面的X的值都是l。·线程B获X的值,首先一级缓存没有命中,然后看二级缓存,二级缓存命中了,所以返X=1;到这里一切都是正常的,因为这时候主内存中也X=l。然后线程B改X的值为2,并将其存放到线程2所在的一级Cache和共享二级Cache中,最后更新主内存中X的值为:到这里一切都是好的。·线程A这次又需要修改X的值,获取时一级缓存命中,并且X=l,到这里问题就出现了,明明线程已经把X的值修改为了2,为何线程A获取的还是l呢?这就是共享变量的内存不可见问题,也就是线程B写入的值对线程A不可见。


4.synchronized关键字介绍

synchronized块Java提供的一种原子性内置锁,Java中的每个对象都可以把它当作一个同步锁来使用,这些Java内置的使用者看不到的锁被称为内部锁,也叫作监视器锁。线程的执行代码在进入synchronized代码块前会自动获取内部锁,这时候其他线程访问该同步代码块时会被阻塞挂起。拿到内部锁的线程会在正常退出同步代码块或者抛出异后或者在同步块内调用了该内置锁资源的wait系列方法时释放该内置锁。内置锁是排它锁,也就是当一个线程获取这个锁后,其他线程必须等待该线程释放锁后才能获取该锁。

另外,由于Java中的线程是与操作系统的原生线程一一对应的,所以当阻塞一个线程时伞,需要从用户态切换到内核态执行阻塞操作,这是很耗时的操作,而synchronized的使用就会导致上下文切换。


5.synchronized的内存语义

这个内存语义就可以解决共享变量内存可见性问题。进入synchronized块的内存语义是把在synchronized块内使用到的变量从线程的工作内存中清除,这样在synchronized块内使用到该变量时就不会从线程的工作内存中获取,而是直接从主内存中获取。退出synchronized块的内存语义是把在synchronized块内对共享变量的修改刷新到主内存。

其实这也是加锁和释放锁的语义,当获取锁后会清空锁块内本地内存中将会被用到的共享变量,在使用这些共享变量时从主内存进行加载,在释放锁时将本地内存中修改的共享变量刷新到主内存。除可以解决共享变量内存可见性问题外,synchronized经常被用来实现原子性操作。另外请注意,synchronized关键字会引起线程上下文切换并带来线程调度开销。


6.volatile关键字

用volatile关键字。该关键字可以确保对一个变量的更新对其他线程马上可见当一个变量被声明为volatile时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存。当其他线程读取该共享变量时,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值。volatile的内存语义和synchronized有相似之处,具体来说就是,当线程写入了volatile变量值时就等价于线程退出synchronized同步块(把写入工作内存的变量值同步到主内存),读取volatie变量值时就相当于进入同步块(先清空本地内存变量值,再从主内存获取最新值)。

volatile虽然提供了可见性保证,但并不保证操作的原子性。那么一般在什么时候才使用volatile关键字呢?

(1)写入变量值不依赖变量的当前值时。因为如果依赖当前值,将是获取一计算一写入三步操作,这三步操作不是原子性的,而volatile不保证原子性。

(2)读写变量值时没有加锁。因为加锁本身已经保证了内存可见性,这时候不需要把变量声明为volatile的。


7.原子性操作

所谓原子性操作,是指执行一系列操作时,这些操作要么全部执行,要么全部不执行,不存在只执行其中一部分的情况。在设计计数器时一般都先读取当前值,

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值