Java并发编程——(二)

1.三个同步原语:synchronized、volatile、final

2.并发编程的两个需要解决的问题:

      *1.线程之间的通信。书中给的定义:线程之间以何种机制来交换信息 。  个人理解:说白了,就是一个共享变量,被多个线程          操作,相互之间能够知道。这也就是内存可见性。

      *2.线程之间如何同步。控制线程间操作共享变量的顺序。

2.并发编程的两个模型:模型:共享内存和消息传递。

      在共享模型中,线程之间的通信是隐式的,通过读写内存中的公共状态来通信。

      在消息传递模型中,线程之间的通信是显示的。线程之间通过发消息来进行通信。

      在共享模型中,线程之间的同步是显示的,需要程序员明确指定某个方法或某段代码在线程之间互斥执行

      在消息传递模型中,线程之间的同步是隐式的。因为消息的发送必须在消息的接受之前。

3.JMM:决定一个线程对共享变量的写入何时对另一个线程可见。定义了线程和主内存之间的抽象关系。在JMM中,每个线程都有自己的本地内存,本地内存中存储了共享变量的副本,读写都在本地内存中进行,等到时机才刷新到主内存中。

4.happen-before原则:

       *1:程序顺序规则:一个线程的每个操作,happen-before该线程的任意后续操作

       *2:监视器规则:一个锁的解锁,一个happen-before随后对这个锁的加锁

       *3:volatile变量规则:对一个volatile变量的写,happen-before于后续任意对这个变量的读

       *4:传递性:A happen-before B,B happen-fore C,那么A happen-before C

      注:当时在程序顺序规则这里,理解有误。既然有程序顺序规则,那不就没有重排序了?后来想想,第一,满足happen-before原则两个操作,并不意味着前一个操作一定要在后一个操作之前执行,只是前一个操作的结果要对后一个操作可见。第二,程序顺序规则是针对单线程。这也是as-if-serial语义的意思,在单线程中,不管怎么重排序,程序的执行结果不能被改变

5.数据依赖性:如果两个操作同时访问一个变量,且其中有一个是写操作,此时这两个操作就存在数据依赖性。

6.单线程中,编译器和处理器会遵守数据依赖性,不对存在数据依赖性的操作重排序。但是多个线程之间或多个处理器之间的数据依赖性,编译器和处理器不会遵守。

7.控制依赖性,因为程序顺序规则和as-if-serial语义的存在,编译器和处理器对存在控制依赖性的代码进行重排序,并不会改变执行结果。但在未正确同步的多线程程序中,执行结果可能会被改变。如下图

8.数据竞争:未正确同步的程序中,一个线程写变量,另一个线程读变量,且写和读没有正确同步。换句话说,就是对存在数据依赖性的代码,多线程操作时没有正确同步。

9.JMM对正确同步的多线程程序做了保证:如果程序是正确同步的,将具有和该程序在顺序一致性模型中一样的执行结果

注:这里需要提醒一下,未正确同步的多线程程序,即使在顺序一致性模型中,执行也是乱序的。只是所有的线程看到的都是这个乱序执行。所以,未正确同步的多线程程序在顺序一致性模型中的执行结果也一定会正确。

10.顺序一致性模型:*1.一个线程中的所有操作必须按照程序的顺序来执行。  *2.不管程序是否同步,所有线程都只能看到一个单一的操作执行顺序。在顺序一致性模型中,所有的操作具有原子性,且立刻对所有线程可见。

11.未正确同步的程序在JMM和顺序一致性模型中的执行有三点不同:*1.未同步的程序在JMM中不保证按程序顺序执行,而在顺序一致性中保证单线程程序按程序顺序执行   *2.JMM不保证所有线程看到的执行顺序一致,而顺序一致性模型保证所有线程看到的都是同一个执行顺序  *3.JMM不保证long/double等64位类型变量的写操作具有原子性。而顺序一致性模型保证所有内存的读写操作都具有原子性   注:第三点,主要是因为总线事务。总线会控制对内存的并发事务,让所有事务串行化执行。在32位处理中对64位的写操作,就会被拆成两个事务。例如一个long类型的写,第一个事务写long类型的高32位,这时另一个线程的事务错误的读取这个long类型高32位的无效值。不过JDK5之后,仅允许把一个long/double类型的变量写操作拆分为两个32为的写操作执行。任意读操作都必须具有原子性。

12.volatile变量具有两个特性:*1.可见性,一个volatile变量的读,总是能看到任意线程对这个变量的最后写入   *2.原子性,对于单个volatile变量的读/写具有原子性,即使它是64位类型的。但对于i++这种复合操作不具备原子性

13.volatile的写内存语义:对于一个volatile变量的写,线程会把本地内存中的共享变量立即刷新到主存中(注:不仅仅是volatile修饰的共享变量,包括在写这个volatile变量前对所有共享变量的修改

14.volatile的读内存语义:对于一个volatile变量的读,线程会被本地内存中的共享变量置为无效,然后从主存中重新读取(注:不仅仅是volatile修饰的变量,包括在读这个volatile变量之前的所有共享变量

15.JMM是通过插入内存屏障来实现volatile的内存语义的。

             *1.第一个操作是volatile读时,无论第二个操作是什么,都不能重排序到volatile读之前

             *2.第二个操作是volatile写时,无论第一个操作是什么,都不能重排序到volatile写之后

             *3.第一个操作是volatile写,第二个操作是volatile读时,不能重排序

16.JMM对volatile变量插入内存屏障的保守策略:

             *1.对每一个volatile读,后面就会加一个LoadLoad和LoadStore屏障

             *2.对每一个volatile写,前面加一个StoreStore屏障,后面加一个StoreLoad屏障

17.JMM对程序员保证,如果一个A操作happen-before B操作,那么A操作的结果对B可见,且A操作的执行顺序排在B操作之前。但JMM对编译器和处理器的限制原则是,只要不改变执行结果,随便怎么优化都行,这就是之前为什么说,A happen-before B,只是执行结果对B可见,不一定要在B之前执行。

18.happen-before原则:

              *1.程序顺序原则:一个线程中的每个操作,happen-before于该线程中的任意后续操作,重点在一个线程。

              *2.监视器原则:对一个锁的解锁,happen-before于随后对这个锁的加锁 

              *3.volatile变量原则:一个volatile变量的写,happen-before于任意后续对这个volatile变量的读

              *4.传递性原则:A happen-before B,B happen-before C,那么A happen-before C

              *5.join()原则:如果在线程A中执行ThreadB.join()并成功返回,那么线程B中的所有操作happen-before于从线程A调用ThreadB.join()到成功返回

               *6.start()原则:如果线程A中执行ThreadB.start(),那么线程A中ThreadB.start()操作happen-before线程B中的所有操作                                 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值