精华内容
下载资源
问答
  • 今天项目上传图片需要校验大小,而且各个地方图片大小的限制不一样,如果在后台控制会...if(s.before==undefined || s.before(e)==undefined || (s.before && s.before(e))) l.parent().submit(); else return false
  • 主要介绍了浅谈spring-boot的单元测试中,@Before不被执行的原因,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • What is the main difference between 之间的主要区别是什么 @Before and @BeforeClass @Before和@BeforeClass

    本文翻译自:Difference between @Before, @BeforeClass, @BeforeEach and @BeforeAll

    What is the main difference between 之间的主要区别是什么

    • @Before and @BeforeClass @Before@BeforeClass
      • and in JUnit 5 @BeforeEach and @BeforeAll 在JUnit 5中@BeforeEach@BeforeAll
    • @After and @AfterClass @After@AfterClass

    According to the JUnit Api @Before is used in the following case: 根据JUnit Api ,在以下情况下使用@Before

    When writing tests, it is common to find that several tests need similar objects created before they can run. 编写测试时,通常会发现几个测试需要先创建类似的对象,然后才能运行。

    Whereas @BeforeClass can be used to establish a database connection. @BeforeClass可用于建立数据库连接。 But couldn't @Before do the same? 但是@Before不能一样吗?


    #1楼

    参考:https://stackoom.com/question/1N9o2/Before-BeforeClass-BeforeEach和-BeforeAll之间的区别


    #2楼

    The code marked @Before is executed before each test, while @BeforeClass runs once before the entire test fixture. 标有@Before的代码在每次测试之前执行,而@BeforeClass在整个测试夹具之前运行一次。 If your test class has ten tests, @Before code will be executed ten times, but @BeforeClass will be executed only once. 如果您的测试类有十个测试,则@Before代码将执行十次,但是@BeforeClass将仅执行一次。

    In general, you use @BeforeClass when multiple tests need to share the same computationally expensive setup code. 通常,当多个测试需要共享相同的计算昂贵的设置代码时,可以使用@BeforeClass Establishing a database connection falls into this category. 建立数据库连接属于此类。 You can move code from @BeforeClass into @Before , but your test run may take longer. 您可以将代码从@BeforeClass移到@Before ,但是您的测试运行可能需要更长的时间。 Note that the code marked @BeforeClass is run as static initializer, therefore it will run before the class instance of your test fixture is created. 注意,标记为@BeforeClass的代码作为静态初始化程序运行,因此它将在创建测试夹具的类实例之前运行。

    In JUnit 5 , the tags @BeforeEach and @BeforeAll are the equivalents of @Before and @BeforeClass in JUnit 4. Their names are a bit more indicative of when they run, loosely interpreted: 'before each tests' and 'once before all tests'. JUnit 5中 ,标签@BeforeEach@BeforeAll与JUnit 4中的@Before@BeforeClass等效。它们的名称更能指示它们的运行时间,松散地解释为:“每次测试之前”和“一次在所有测试之前”。 '。


    #3楼

    Difference between each annotation are : 每个注释之间的区别是:

    +-------------------------------------------------------------------------------------------------------+
    ¦                                       Feature                            ¦   Junit 4    ¦   Junit 5   ¦
    ¦--------------------------------------------------------------------------+--------------+-------------¦
    ¦ Execute before all test methods of the class are executed.               ¦ @BeforeClass ¦ @BeforeAll  ¦
    ¦ Used with static method.                                                 ¦              ¦             ¦
    ¦ For example, This method could contain some initialization code          ¦              ¦             ¦
    ¦-------------------------------------------------------------------------------------------------------¦
    ¦ Execute after all test methods in the current class.                     ¦ @AfterClass  ¦ @AfterAll   ¦
    ¦ Used with static method.                                                 ¦              ¦             ¦
    ¦ For example, This method could contain some cleanup code.                ¦              ¦             ¦
    ¦-------------------------------------------------------------------------------------------------------¦
    ¦ Execute before each test method.                                         ¦ @Before      ¦ @BeforeEach ¦
    ¦ Used with non-static method.                                             ¦              ¦             ¦
    ¦ For example, to reinitialize some class attributes used by the methods.  ¦              ¦             ¦
    ¦-------------------------------------------------------------------------------------------------------¦
    ¦ Execute after each test method.                                          ¦ @After       ¦ @AfterEach  ¦
    ¦ Used with non-static method.                                             ¦              ¦             ¦
    ¦ For example, to roll back database modifications.                        ¦              ¦             ¦
    +-------------------------------------------------------------------------------------------------------+
    

    Most of annotations in both versions are same, but few differs. 两个版本中的大多数注释都相同,但几乎没有区别。

    Reference 参考

    Order of Execution. 执行顺序。

    Dashed box -> optional annotation. 虚线框->可选注释。

    在此处输入图片说明


    #4楼

    Before and BeforeClass in JUnit JUnit中的BeforeClass和BeforeClass

    The function @Before annotation will be executed before each of test function in the class having @Test annotation but the function with @BeforeClass will be execute only one time before all the test functions in the class. @Before注释函数将在具有@Test注释的类中的每个测试函数之前执行,而具有@BeforeClass的函数仅在类中的所有测试函数之前执行一次。

    Similarly function with @After annotation will be executed after each of test function in the class having @Test annotation but the function with @AfterClass will be execute only one time after all the test functions in the class. 类似地,具有@After批注的函数将在类中具有@Test批注的每个测试函数之后执行,但是具有@AfterClass的函数仅在该类中的所有测试函数之后执行一次。

    SampleClass 样本类

    public class SampleClass {
        public String initializeData(){
            return "Initialize";
        }
    
        public String processDate(){
            return "Process";
        }
     }
    

    SampleTest 样品测试

    public class SampleTest {
    
        private SampleClass sampleClass;
    
        @BeforeClass
        public static void beforeClassFunction(){
            System.out.println("Before Class");
        }
    
        @Before
        public void beforeFunction(){
            sampleClass=new SampleClass();
            System.out.println("Before Function");
        }
    
        @After
        public void afterFunction(){
            System.out.println("After Function");
        }
    
        @AfterClass
        public static void afterClassFunction(){
            System.out.println("After Class");
        }
    
        @Test
        public void initializeTest(){
            Assert.assertEquals("Initailization check", "Initialize", sampleClass.initializeData() );
        }
    
        @Test
        public void processTest(){
            Assert.assertEquals("Process check", "Process", sampleClass.processDate() );
        }
    
    }
    

    Output 输出量

    Before Class
    Before Function
    After Function
    Before Function
    After Function
    After Class
    

    In Junit 5 在Junit 5中

    @Before = @BeforeEach
    @BeforeClass = @BeforeAll
    @After = @AfterEach
    @AfterClass = @AfterAll
    

    #5楼

    import org.junit.Assert
    import org.junit.Before
    import org.junit.BeforeClass
    import org.junit.Test
    
    class FeatureTest {
        companion object {
            private lateinit var heavyFeature: HeavyFeature
            @BeforeClass
            @JvmStatic
            fun beforeHeavy() {
                heavyFeature = HeavyFeature()
            }
        }
    
        private lateinit var feature: Feature
    
        @Before
        fun before() {
            feature = Feature()
        }
    
        @Test
        fun testCool() {
            Assert.assertTrue(heavyFeature.cool())
            Assert.assertTrue(feature.cool())
        }
    
        @Test
        fun testWow() {
            Assert.assertTrue(heavyFeature.wow())
            Assert.assertTrue(feature.wow())
        }
    }
    

    Same as 如同

    import org.junit.Assert
    import org.junit.Test
    
     class FeatureTest {
        companion object {
            private val heavyFeature = HeavyFeature()
        }
    
        private val feature = Feature()
    
        @Test
        fun testCool() {
            Assert.assertTrue(heavyFeature.cool())
            Assert.assertTrue(feature.cool())
        }
    
        @Test
        fun testWow() {
            Assert.assertTrue(heavyFeature.wow())
            Assert.assertTrue(feature.wow())
        }
    }
    
    展开全文
  • 详细介绍了JMM Java内存模型的概念、由来,以及happens-before原则的具体规则。

    详细介绍了JMM Java内存模型的概念、由来,以及happens-before原则的具体规则。

    Java内存模型(Java Memory Model,JMM)是java虚拟机规范定义的一组规范以及机制,本身是一种抽象的概念,并不真实存在。JMM的目标是通过控制主内存与每个线程的本地内存(工作内存)之间的交互,来为 Java 程序员提供内存可见性保证,以求多个线程能够正确的访问共享变量。Java是使用具体、改良的更好理解的happens-before原则来实现这一目标。

    1 并发编程的两个问题

    并发编程需要处理的两个关键问题是:线程之间如何通信 和 线程之间如何同步。

    1.1 通信

    通信是指线程之间以何种机制来交换信息。

    1. 命令式编程中,线程之间的通信机制有两种,是 共享内存 和 消息传递。
    2. 共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的 公共状态 来隐式进行通信。典型的共享内存通信方式就是通过共享对象进行通信。

    消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的 发送消息 来显式进行通信。在java中典型的消息传递方式就是wait()和notify()。

    1.2 同步

    同步是指程序用于控制不同线程之间操作发生相对顺序的机制。

    1. 共享内存的并发模型里,同步是 显式 进行的。程序员必须显式指定某个方法或某段代码需要在线程之间 互斥执行。
    2. 消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是 隐式 进行的。

    Java 的并发采用的是 共享内存模型,线程之间的通信对程序员完全透明。同步的底层使用的是临界区对象,是指当使用某个线程访问共享资源时,必须使代码段独享该资源,不允许其他线程访问该资源。

    2 Java内存模型

    2.1 内存模型的抽象

    “内存模型”一词,可以理解为在特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象,它定义了共享内存系统中多线程程序读写操作行为的规则,通过这些规则来规范对内存的读写操作,从而保证指令执行的正确性。 它与处理器有关、与缓存有关、与并发有关、与编译器也有关。它解决了CPU多级缓存、处理器优化、指令重排等导致的内存访问问题,保证了并发场景下共享内存数据的正确性(一致性、原子性和有序性)。

    不同架构的物理计算机可以有不一样的内存模型,Java虚拟机是一个完整的计算机的一个模型,因此这个模型自然也包含一个内存模型——又称为Java内存模型,并且它的内存访问操作与硬件的缓存访问操作具有很高的可比性。

    Java内存模型(Java Memory Model,JMM)是java虚拟机规范定义的一组规范以及机制 ,本身是一种抽象的概念,并不真实存在。它屏蔽掉了各种硬件和操作系统的内存访问差异,让Java程序在各种平台下都能达到内存访问的一致性(可见性、有序性、原子性),不必因为不同平台上的物理机的内存模型的差异,对各平台定制化开发程序。

    原始的Java内存模型效率并不是很理想,因此Java1.5版本对其进行了重构,现在的Java8仍沿用了Java1.5的版本。

    2.2 主内存与工作内存

    Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节,比如一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。此处的变量(Variables)与Java编程中所说的变量有所区别,它包括了实例字段、静态字段和构成数组对象的元素,但不包括局部变量与方法参数。

    JMM定义了线程和主内存之间的抽象关系:所有的共享变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件时的主内存名字一样,两者也可以互相类比,但此处仅是虚拟机内存的一部分)。每条线程还有自己的工作内存(Working Memory,可与前面讲的处理器高速缓存类比,但只是一个抽象概念,物理上不存在),线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝(对于引用类型,可能拷贝对象的引用或者某个字段,但不会把这个对象整个拷贝一次),线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(对于volatile变量依然有工作内存的拷贝,但是由于它特殊的操作顺序的规定,所以看起来如同直接在内存中读写一般)。

    不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。JMM通过控制主内存与每个线程的本地内存(工作内存)之间的交互,来为 Java 程序员提供内存可见性保证。

    线程、主内存、工作内存三者的交互关系如图:

    在这里插入图片描述

    这里所讲的主内存、工作内存与Java内存区域中的Java堆、栈、方法区等并不是同一个层次的内存划分,这两者基本上是没有关系的,如果两者一定要勉强对应起来,那从变量、主内存、工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域。从更低层次上说,主内存就直接对应于物理硬件的内存,而为了获取更好的运行速度,虚拟机(甚至是硬件系统本身的优化措施)可能会让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问读写的是工作内存。

    2.3 内存间的交互操作

    物理机高速缓存和主内存之间的交互有协议,同样的,Java也有关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存之类的实现细节,java内存模型中定义了8种操作来完成交互,虚拟机实现时必须保证这8种操作都是原子的、不可分割的(对于long和double类型的变量来说,load、store、read跟write在某些32位虚拟机平台上允许例外)。

    1. lock(锁定):作用于主存的变量,把一个变量标识为一条线程独占状态,一个变量在同一时间只能一个线程锁定。
    2. unlock(解锁):作用于主存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
    3. read(读取):作用于主存变量,把一个变量的值从主存传输到工作内存,以便随后的load操作使用。
    4. load(载入):作用于工作内存变量,把 read 操作从主内存中读取的变量的值放入工作内存的变量副本中(副本是相对于主内存的变量而言的)。
    5. use(使用):作用于工作内存变量,把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时就会执行该操作。
    6. assign(赋值):作用于工作内存变量,它把一个从执行引擎接收到的值赋值给工作内存变量,每当引擎遇到一个给变量赋值的字节码指令的时候即执行此操作;
    7. store(存储):作用于工作内存变量,把工作内存中一个变量的值传送到主存,以便随后的write操作使用。
    8. write(写入):作用于主存变量,把store操作从工作内存中得到的变量的值放入主存的变量中。

    在这里插入图片描述

    在这里插入图片描述

    2.4 交互操作的同步规则

    JMM 在执行前面介绍的 8 种基本操作时,为了保证内存间数据一致性,JMM 中规定需要满足以下规则:

    1. 如果要把一个变量从主存复制到工作内存:顺序执行 read 和 load 操作。如果要把变量从工作内存同步会主存:顺序执行 store 和 write 操作。上述的操作是指jvm规定对一个主内存操作的时候需要进行的步骤,其中lock和unlock可以通过字节码指令和咱们的并发包,而对于lock和unlock对变量的操作底层涉及到内存屏障。JMM 只是规定了必须顺序执行,而没有保证是连续执行,其间可以插入其他指令。
    2. 不允许read和load、store和write操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者从工作内存发起回写了但主内存不接受的情况。
    3. 不允许线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步到主内存中。
    4. 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存。
    5. 一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说,就是对一个变量实施use、store之前,必须先执行过了assign和load操作。
    6. 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一线程重复执行多次,多次执行lock之后,只有执行相同次数的unlock操作,变量才会被解锁。
    7. 如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量之前,需要重新执行load或assign操作初始化变量的值。
    8. 如果一个变量事先没有被lock操作锁定,那就不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定住的变量。
    9. 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)。

    如上9种内存访问操作以及规则限定,再加上对volatile的一些特殊规定,就已经完全确定了java程序中哪些内存访问操作是在并发下安全的,以上的规则等效于happens-before(先行发生)原则。

    3 改良的happens-before原则

    3.1 JMM的设计意图

    从JMM设计者的角度,在设计JMM时,需要考虑两个关键因素:

    1. 程序员对内存模型的使用。程序员希望内存模型易于理解、易于编程。程序员希望基于一个强内存模型来编写代码。
    2. 编译器和处理器对内存模型的实现。编译器和处理器希望内存模型对它们的束缚越少越好,这样它们就可以做尽可能多的优化来提高性能。编译器和处理器希望实现一个弱内存模型。

    由于这两个因素互相矛盾,所以JSR-133专家组在设计JMM时的核心目标就是找到一个好的平衡点:一方面,要为程序员提供足够强的内存可见性保证;另一方面,对编译器和处理器的限制要尽可能地放松。JSR-133使用改良的happens-before原则来实现这一目标。 关于Java内存模型和顺序一致性内存模型、原始happens-before内存模型的关系,可以参考:JMM与顺序一致模型和Happens-Before模型的关系介绍

    如下一段代码:

    double pi = 3.14;   // A
    double r = 1.0;     // B
    double area = pi * r * r;  // C
    

    上面计算圆的面积的示例代码存在3个happens-before关系,如下。

    1. A happens-before B。
    2. B happens-before C。
    3. A happens-before C。

    在3个happens-before关系中,2和3是必需的,但1是不必要的。因此,JMM把happens-before要求禁止的重排序分为了下面两类。

    1. 会改变程序执行结果的重排序。
    2. 不会改变程序执行结果的重排序。

    JMM对这两种不同性质的重排序,采取了不同的策略,如下。

    1. 对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序。
    2. 对于不会改变程序执行结果的重排序,JMM对编译器和处理器不做要求(JMM允许这种重排序)。

    下面是JMM的设计示意图:

    在这里插入图片描述

    从上图可以看出两点:

    1. JMM 向程序员提供的 happens-before 规则能满足程序员的需求。 JMM 的 happens-before规则不但简单易懂,而且也向程序员提供了足够强的内存可见性保证(有些内存可见性保证其实并不一定真实存在,比如上面的 A happens- before B)。
    2. JMM 对编译器和处理器的束缚已经尽可能的少。 从上面的分析我们可以看出,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。 比如,如果编译器经过细致的分析后,认定一个锁只会被单个线程访问,那么这个锁可以被消除。再比如,如果编译器经过细致的分析后,认定一个 volatile 变量仅仅只会被单个线程访问,那么编译器可以把这个 volatile变量当作一个普通变量来对待。这些优化既不会改变程序的执行结果,又能提高程序的执行效率。

    3.2 改良的happens-before的定义

    从JDK 1.5开始,Java使用新的JSR-133内存模型。现在的Java内存模型是建立在happens-before(先行发生)内存模型而不是顺序一致性内存模型之上的,并且再此基础上,增强了一些。因为happens-before(先行发生)内存模型是一个弱约束的内存模型,在多线程竞争访问共享数据的时候,会导致不可预期的结果,这些结果有一些是java内存模型可以接受的,有一些是java内存模型不可以接受的。

    JSR-133使用happens-before的概念来指定两个操作之间的执行顺序,阐述操作之间的内存可见性。 由于这两个操作可以在一个线程之内,也可以是在不同线程之间。因此,JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证。

    在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。

    《JSR-133:JavaTM内存模型与线程规范》对happens-before关系的定义如下:

    1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
    2. 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么这种重排序并不非法(也就是说,JMM允许这种重排序)。

    上面的1)是JMM对程序员的承诺。 从程序员的角度来说,可以这样理解happens-before关系:如果A happens-before B,那么Java内存模型将向程序员保证——A操作的结果将对B可见,且A的执行顺序排在B之前。注意,这只是Java内存模型向程序员做出的保证,实际上并不一定执行顺序如预期!

    上面的2)是JMM对编译器和处理器重排序的约束原则。 正如前面所言,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。 JMM这么做的原因是:程序员对于这两个操作是否真的被重排序并不关心,程序员关心的是程序执行时的语义不能被改变(即执行结果不能被改变)。因此,happens-before关系本质上和as-if-serial语义是一回事。

    3.3 as-if-serial 和 happens-before

    as-if-serial 语义保证单线程内程序的执行结果不被改变,happens-before 关系保证正确同步的多线程程序的执行结果不被改变。

    as-if-serial 语义给编写单线程程序的程序员创造了一个幻境:单线程程序是按程序的顺序来执行的。happens-before 关系给编写正确同步的多线程程序的程序员创造了一个幻境:正确同步的多线程程序是按 happens-before 指定的顺序来执行的。

    as-if-serial 语义和 happens-before 这么做的目的,都是为了在不改变程序执行结果的前提下,尽可能地提高程序执行的效率。

    3.4 单线程和多线程的happens-before

    单线程下的 happens-before,字节码的先后顺序天然包含 happens-before 关系:因为单线程内共享一份工作内存,不存在数据一致性的问题。

    在程序控制流路径中靠前的字节码 happens-before 靠后的字节码,即靠前的字节码执行完之后操作结果对靠后的字节码可见。然而,这并不意味着前者一定在后者之前执行。实际上,如果后者不依赖前者的运行结果,那么它们可能会被重排序。

    多线程下的 happens-before,多线程由于每个线程有共享变量的副本,如果没有对共享变量做同步处理,线程 1 更新执行操作 A 共享变量的值之后,线程 2 开始执行操作 B,此时操作 A 产生的结果对操作 B 不一定可见。也就不满足happens-before了。

    3.5 Java内存模型中具体的happens-before原则

    下面是Java内存模型下一些“天然的”先行发生关系,这些先行发生关系无须任何同步器协助就已经存在,可以在编码中直接使用。如果两个操作之间的关系不在此列,并且无法从下列规则推导出来的话,它们就没有顺序性保障,虚拟机可以对它们随意地进行重排序。

    1. 程序次序规则(Program Order Rule):在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。准确地说,应该是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。
    2. 管程锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作。这里必须强调的是同一个锁,而“后面”是指时间上的先后顺序。
    3. volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读操作,这里的“后面”同样是指时间上的先后顺序。
    4. 线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。
    5. 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
    6. 线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。
    7. 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。
    8. 传递性(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。
      根据上面的happens-before规则,显然,一般只需要使用volatile关键字,或者使用锁的机制,就能实现内存的可见性了。

    注意:

    1. 两个操作之间存在 happens-before 关系,并不意味着 Java 平台的具体实现必须要按照 happens-before 关系指定的顺序来执行。如果重排序之后的执行结果,与按 happens-before 关系来执行的结果一致,即前一个操作(执行的结果)对后一个操作可见,那么这种重排序并不非法(也就是说,JMM 允许这种重排序)。
    2. JMM 其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。JMM 这么做的原因是:程序员对于这两个操作是否真的被重排序并不关心,程序员关心的是程序执行时的语义不能被改变(即执行结果不能被改变)。因此,happens-before 关系本质上和 as-if-serial 语义是一回事。

    3.6 happens-before与JMM的关系

    在这里插入图片描述

    如上图,一个happens-before规则对应于一个或多个编译器和处理器重排序规则。对于Java程序员来说,happens-before规则简单易懂,它避免Java程序员为了理解JMM提供的内存可见性保证而去学习复杂的重排序规则以及这些规则的具体实现方法。

    在计算机中,软件技术和硬件技术有一个共同的目标:在不改变程序执行结果的前提下,尽可能提高并行度。编译器和处理器遵从这一目标,从happens-before的定义我们可以看出,JMM同样遵从这一目标。

    参考资料:

    1. JSR133规范
    2. 《深入理解java虚拟机》
    3. 《Java并发编程之美》
    4. 《Java并发编程的艺术》
    5. 《实战Java高并发程序设计》

    如果有什么不懂或者需要交流,可以留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

    展开全文
  • 1. 不同注解的区别如下 特性 Junit 4 Junit 5 ... @BeforeAll 在当前类中的所有测试方法之后执行。 注解在静态方法上。 此方法可以包含一些清理代码。 ...

    1. 不同注解的区别如下

    特性

    Junit 4Junit 5
    • 在当前类的所有测试方法之前执行。
    • 注解在静态方法上。
    • 此方法可以包含一些初始化代码。
    @BeforeClass@BeforeAll
    • 在当前类中的所有测试方法之后执行。
    • 注解在静态方法上。
    • 此方法可以包含一些清理代码。
    @AfterClass@AfterAll
    • 在每个测试方法之前执行。
    • 注解在非静态方法上。
    • 可以重新初始化测试方法所需要使用的类的某些属性。
    @Before@BeforeEach
    • 在每个测试方法之后执行。
    • 注解在非静态方法上。
    • 可以回滚测试方法引起的数据库修改。
    @After

    @AfterEach

     

    展开全文
  • Happens-Before保证线程可见

    千次阅读 2020-03-31 21:07:03
    熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。 按照官方的说法: 当一个变量被多个线程读取并且...

    前言

    熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。

    按照官方的说法:

    当一个变量被多个线程读取并且至少被一个线程写入时,如果读操作和写操作没有 happens-before 关系,则会产生数据竞争问题。

    要想保证操作 B 的线程看到操作 A 的结果(无论 AB 是否在一个线程),那么在 AB 之间必须满足 HB 原则,如果没有,将有可能导致重排序。

    当缺少 HB 关系时,就可能出现重排序问题。

    happens-before有哪些规则?

    happens-before关系主要强调两个有冲突的动作之间的顺序,以及定义数据争用的发生时间。

    • 程序顺序规则:某个线程中的每个动作都happens-before该线程中该动作后面的动作。
    • 监视器锁规则:某个管程(对象锁)上的unlock动作happens-before同一个管程上后续的lock动作。
    • volatile变量规则:对某个volatile字段的写操作happens-before每个后续对该volatile字段的读操作。
    • Join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
    • 传递性:如果某个动作a happens-before动作b,且b happens-before动作c,则有a happens-before c。
    • start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
    • 程序中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。
    • 对象finalize规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。

    然后,再换个角度解释 happens-before:当一个操作 A happens-before 操作 B,那么,操作 A 对共享变量的操作结果对操作 B 都是可见的。同时,如果操作 B happens-before 操作 C,那么操作 A 对共享变量的操作结果对操作 B 都是可见的。

    而实现可见性的原理则是 cache protocol 和 memory barrier。通过缓存一致性协议和内存屏障实现可见性。

    如何实现同步?

    在 Doug Lea 著作 《Java Concurrency in Practice》中,有下面的描述:

    由于happens-before的排序功能很强大,因此有时候可以“借助(Piggyback)”现有同步机制的可见性属性。这需要将happens-before的程序顺序规则与其他某个顺序规则(通常是监视器或volatile变量规则)结合起来,从而对某个未被锁保护的变量的访问操作进行排序。这项技术由于对语句的顺序非常敏感,因此很容易出错。它是一项高级技术,并且只有当需要最大限度地提升某些类(例如ReetrantLock)的性能时,才应该使用这项技术。

    书中提到:通过组合happens-before 的一些规则,可以实现对某个未被锁保护变量的可见性。但由于这个技术对语句的顺序很敏感,因此容易出错

    下面,将演示如何通过 volatile 规则和程序次序规则实现对一个变量同步。

    /**
    * 两个线程间隔打印出 0 – 100 的数字
    **/
    class ThreadPrintDemo {
    
      static int num = 0;
      static volatile boolean flag = false;
    
      public static void main(String[] args) {
    
        Thread t1 = new Thread(() -> {
          for (; 100 > num; ) {
            if (!flag && (num == 0 || ++num % 2 == 0)) {
              System.out.println(num);
              flag = true;
            }
          }
        }
        );
    
        Thread t2 = new Thread(() -> {
          for (; 100 > num; ) {
            if (flag && (++num % 2 != 0)) {
              System.out.println(num);
              flag = false;
            }
          }
        }
        );
    
        t1.start();
        t2.start();
      }
    }
    

    这个 num 变量没有使用 volatile,会有可见性问题,即:t1线程更新了num,t2线程无法感知。

    其实刚开始也是这么认为的,但最近通过研究 HB 规则,发现去掉 num 的 volatile 修饰也是可以的。

    我们分析一下,如下图所示:

    在这里插入图片描述

    我们分析这个图:

    1. 首先,红色和黄色表示不同的线程操作。
    2. 红色线程对 num 变量做 ++,然后修改了 volatile 变量,这个是符合程序顺序规则的。也就是 1 happens-before 2.
    3. 红色线程对 volatile 的写happens-before 黄色线程对 volatile 的读,也就是 2 happens-before 3.
    4. 黄色线程读取 volatile 变量,然后对 num 变量做 ++,符合程序顺序规则,也就是 3 happens-before 4.
    5. 根据传递性规则,1 肯定 happens-before 4. 所以,1 的修改对 4来说都是可见的。

    注意:happens-before 规则保证上一个操作的结果对下一个操作都是可见的。所以,上面的小程序中,线程 A 对 num 的修改,线程 B 是完全感知的 —— 即使 num 没有使用 volatile 修饰。

    这样,我们就借助 happens-before 原则实现了对一个变量的同步操作,也就是在多线程环境中,保证了并发修改共享变量的安全性。并且没有对这个变量使用 Java 的原语:volatile 和 synchronized 和 CAS(假设算的话)。

    这可能看起来不安全(实际上安全),也好像不太容易理解。因为这一切都是 happens-before 底层的 cache protocol 和 memory barrier 实现的。

    其他规则实现同步

    1. 利用线程终结规则实现:

      static int a = 1;
    
      public static void main(String[] args) {
        Thread tb = new Thread(() -> {
          a = 2;
        });
        Thread ta = new Thread(() -> {
          try {
            tb.join();
          } catch (InterruptedException e) {
            //NO
          }
          System.out.println(a);
        });
    
        ta.start();
        tb.start();
      }
    

    2. 利用线程 start 规则实现:

     static int a = 1;
    
      public static void main(String[] args) {
        Thread tb = new Thread(() -> {
          System.out.println(a);
        });
        Thread ta = new Thread(() -> {
          tb.start();
          a = 2;
        });
    
        ta.start();
      }
    

    这两个操作,也可以保证变量 a 的可见性。

    确实有点颠覆之前的观念。之前的观念中,如果一个变量没有被 volatile 修饰或 final 修饰,那么他在多线程下的读写肯定是不安全的(因为会有缓存),导致读取到的不是最新的。

    然而,通过借助 happens-before,我们可以实现。

    总结

    虽然本文标题是通过 happen-before 实现对共享变量的同步操作,但主要目的还是更深刻的理解 happen-before,理解他的 happen-before 概念其实就是保证多线程环境中,上一个操作对下一个操作的有序性和操作结果的可见性。

    同时,通过灵活的使用传递性规则,再对规则进行组合,就可以将两个线程进行同步— 实现指定的共享变量不使用原语也可以保证可见性。虽然这好像不是很易读,但也是一种尝试。

    关于如何组合使用规则实现同步,Doug Lea 在 JUC 中给出了实践。

    例如老版本的 FutureTask 的内部类 Sync(已消失),通过 tryReleaseShared 方法修改 volatile 变量,tryAcquireShared 读取 volatile 变量,这是利用了 volatile 规则;

    通过在 tryReleaseShared 之前设置非 volatile 的 result 变量,然后在 tryAcquireShared 之后读取 result 变量,这是利用了程序次序规则。

    从而保证 result 变量的可见性。和我们的第一个例子类似:利用程序次序规则和 volatile 规则实现普通变量可见性。

    而 Doug Lea 自己也说了,这个“借助”技术非常容易出错,要谨慎使用。但在某些情况下,这种“借助”是非常合理的。

    实际上,BlockingQueue 也是“借助”了 happen-before 的规则。还记得 unlock 规则吗?当 unlock 发生后,内部元素一定是可见的。

    而类库中还有其他的操作也“借助”了 happen-before 原则:并发容器,CountDownLatch,Semaphore,Future,Executor,CyclicBarrier,Exchanger 等。

    总而言之,言而总之:

    happen-before 原则是JMM的核心所在,只有满足了happens-before原则才能保证有序性和可见性,否则编译器将会对代码重排序。happen-before甚至将lock和volatile也定义了规则。

    通过适当的对 happen-before规则的组合,可以实现对普通共享变量的正确使用。

    参考链接:https://www.01hai.com/note/av144549​

    展开全文
  • as-if-serial规则和happens-before规则的区别

    千次阅读 多人点赞 2019-10-04 15:17:39
    为了在不改变程序执行结果的前提下,尽可能地提高程序执行的并行度,我们需要了解as-if-serial规则和happens-before规则 as-if-serial规则 as-if-serial语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度...
  • ::before和::after伪元素的用法

    千次阅读 多人点赞 2019-06-16 22:56:30
    一、介绍 css3为了区分伪类和伪元素,伪元素采用双冒号写法。 常见伪类——:hover,:link,:active,:target,:not(),:focus。...::before和::after下特有的content,用于在css渲染中向元素逻辑上的头部或尾部添加内容...
  • 详细整理了oracle中before和after的使用问题
  • Junit5废弃@Before和@After注解

    千次阅读 2020-02-19 11:39:04
    在使用Spring Boot最新版本进行单元测试时,发现@Before和@After注解无法使用。追溯原因,Spring Boot在2.x版本中已经使用了Junit5来作为单元测试的支持。 而在Junit5中,@Before和@After注解被@BeforeEach和@After...
  • before和after的用法

    万次阅读 2018-03-30 14:51:12
    (:before和:after是在CSS2中提出来的,CSS3中的写法是::before和::after)常见伪类——:hover,:link,:active,:target,:not(),:focus。常见伪元素——::first-letter,::first-line,::before,::after,::selection。::be...
  • 什么是happens-before原则?

    千次阅读 热门讨论 2021-06-07 22:53:02
    happens-before 关系是用来描述两个操作的内存可见性的。如果操作 X happens-before 操作 Y,那么 X 的结果对于 Y 可见。 Java 内存模型底层是通过内存屏障(memory barrier)来禁止重排序的。 线程内的 happens-...
  • Connection has been closed BEFORE response异常

    万次阅读 热门讨论 2019-12-14 21:36:20
    reactor.netty.channel.AbortedException: Connection has been closed BEFORE response 网上狂搜,以我以往的解决思路,欲解决问题,首先复现问题,然后再解决问题,这种最放心,若是实在无法复...
  • vue-router中的beforeEach

    千次阅读 2020-09-02 14:31:15
    动态路由处理方式 因为每次在进行路由跳转的时候,都会触发对应的钩子函数,可以参考官网的文档注解: 本人在项目里面是运用了beforeEach这个钩子函数来监控路由的变化的,具体可以参看代码: beforeEach实现的思路...
  • date的before方法 日期类before()方法 (Date Class before() method) before() method is available in java.util package. before()方法在java.util包中可用。 before() method is used to check whether this date...
  • happen-before是什么

    2020-11-03 17:04:41
    目录happen-before是什么happen-before出现的原因happen-before的七条原则happen-before的传递性 happen-before是什么 happen-before出现的原因 为了明确定义多线程场景下重排序的问题,Java引入了JMM(Java Memory ...
  • expected initializer before

    千次阅读 2019-10-22 15:22:50
    原因:某个地方缺少分号 如: void print(int a) { int b ///wrong here std::cout << a << std::endl; } 解决:重点排查报错行前几行的变量声明等。
  • 1、错误:UnboundLocalError: local variable 'XXX' referenced before assignment 错误: UnboundLocalError:在赋值之前引用了局部变量“XXX” 例: def test(): i=i+1 print("global i = ",i) i=0 test...
  • 目录 官方图 理解beforeUpdate:视图层的数据改变时触发 ... 这里需要注意的是beforeUpdate是在data数据被改变后触发,这里的data数据改变如何理解,仅从字面上理解只要data数据值改变就会触发beforeUpdate..
  • happens-before规则和as-if-serial语义先行发生原则(happens-before)什么是happens-beforehappens-before规则示例as-if-serial语义总结 先行发生原则(happens-before) 我们知道,在Java内存模型中,如果要确保有序性...
  • happens-before 规则

    万次阅读 2018-05-21 13:42:57
    1.happens-before 定义 happens-before的概念最初由Leslie Lamport在其一篇影响深远的论文(《Time,Clocks and the Ordering of Events in a Distributed System》)中提出,有兴趣的可以google一下。JSR-133使用...
  • 【Flask】before_first_request与processes

    千次阅读 2020-06-30 20:04:12
    first_request初始化删除所有文件 既然运行关键字函数后删除代码文件的操作没有问题,那么问题很大概率时因为before_first_request的请求 before_first_request 查看这段代码 # @app.before_first_request def ...
  • 阿飞云2019-06-10 23:16:38 做一个积极的人 编码、改bug、提升自己 ...用过spring框架进行开发的人,多多少少会使用过它的AOP功能,都知道有@Before、@Around和@After等advice。最近,为了实现项目中的输出日...
  • Java并发编程之happens-before和as-if-serial语义

    千次阅读 多人点赞 2019-02-27 15:19:05
    Java并发编程之happens-before和as-if-serial语义
  • 通俗易懂happens-before理解

    千次阅读 2018-12-13 23:02:28
    在JMM(JAVA内存模型)中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。 JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证(如果A线程的写操作a与...
  • java 8大happen-before原则超全面详解

    千次阅读 2018-10-23 21:39:21
    单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。 锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。 volatile的happen-before原则:对一个...
  • happen-before原则

    万次阅读 多人点赞 2018-10-09 23:27:30
    happen-before是JMM最核心的概念,所以在了解happen-before原则之前,首先需要了解java的内存模型。 JMM内存模型 &nbsp;&nbsp;&nbsp;&nbsp; java内存模型是共享内存的并发模型,线程之间主要通过读-...
  • CSS3 : 伪元素 ::before 和 ::after的用法介绍

    万次阅读 多人点赞 2016-04-21 14:43:33
    ::before 和 ::after 是用来给元素添加额外内容的,因为只存在于作用元素内容的前后 ::before 和 ::after 内部的content支持以下三种特性! 字符串[“可以是符号什么的也可以是单纯的字符” ,支持 unicode编码 ...
  • AOP中有@Before,@After,@Around,@AfterRunning注解等等。 首先上下自己的代码,定义了切点的定义 @Aspect @Component public class LogApsect { private static final Logger logger = LoggerFactory....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,156,234
精华内容 462,493
关键字:

before