3妹插: 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。
坚持不懈,越努力越幸运,大家一起学习鸭~~~
2哥:3妹,在干什么呢。
3妹:我在学习呢, 这不是最近工作中用到Java了嘛,要抓紧时间上手了。
2哥:学习的怎么样啦?不用的话我倒是可以指点指点。
3妹:还真有点搞不懂,这个happens-before关系到底是怎样的,你给我讲讲呗。
2哥:可以是可以,我有什么好处呢?
3妹:好啦,我学会了后请你吃饭怎么样?
2哥:就等你这句呢,我要吃北京烤鸭!
3妹:可以!!!
概述
由于存在线程本地内存和主内存的原因,再加上重排序,会导致多线程环境下存在可见性的问题。那么我们正确使用同步、锁的情况下,线程A修改了变量a何时对线程B可见?
我们无法就所有场景来规定某个线程修改的变量何时对其他线程可见,但是我们可以指定某些规则,这规则就是happens-before,从JDK 5 开始,JMM就使用happens-before的概念来阐述多线程之间的内存可见性。
在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。
happens-before原则定义
happens-before原则非常重要,它是判断数据是否存在竞争、线程是否安全的主要依据,依靠这个原则,我们解决在并发环境下两操作之间是否可能存在冲突的所有问题。下面我们就一个简单的例子稍微了解下happens-before ;
i = 1; //线程A执行
j = i ; //线程B执行
j 是否等于1呢?假定线程A的操作(i = 1)happens-before线程B的操作(j = i),那么可以确定线程B执行后j = 1 一定成立,如果他们不存在happens-before原则,那么j = 1 不一定成立。这就是happens-before原则的威力。
happens-before原则定义如下:
- 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
- 两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。
happens-before 原则规则:
1.程序次序性规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。一段代码在单线程中执行的结果是有序的。(只在单线程有效,一写多读时,写的变量如果是没有使用volatile修饰时,是没法保证其他线程读到的变量是最新的值)
2.锁定规则:一个锁的解锁操作总是要在加锁操作之前。
3.volatile规则:如果一个写操作去写一个volatile变量,一个读操作去读volatile变量,那么写操作一定是在读操作之前。
4.传递规则:操作A happens before 操作B, B happens before C,那么A一定是happens before C的。
5.线程启动规则:线程A执行过程中修改一些共享变量,然后再调用threadB.start()方法来启动线程B,那么A对那些变量的修改对线程B一定是可见的。
6.线程终结规则:线程A执行过程中调用了threadB.join()方法来等待线程B执行完毕,那么线程B在执行过程中对共享变量的修改,在join()方法返回后,对线程A可见。
7.线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
8.对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
上面八条是原生Java满足Happens-before关系的规则,但是我们可以对他们进行推导出其他满足happens-before的规则:
1.将一个元素放入一个线程安全的队列的操作Happens-Before从队列中取出这个元素的操作
2.将一个元素放入一个线程安全容器的操作Happens-Before从容器中取出这个元素的操作
3.在CountDownLatch上的倒数操作Happens-Before CountDownLatch#await()操作
4.释放Semaphore许可的操作Happens-Before获得许可操作
5.Future表示的任务的所有操作Happens-Before Future#get()操作
6.向Executor提交一个Runnable或Callable的操作Happens-Before任务开始执行操作
网友评论