石家庄哪里有网站推广,中小型网站建设案例,创意网站交互,花生壳网站建设文章目录 一、happens-before的定义二、happens-before的规则1. 程序顺序规则#xff1a;2. 监视器锁规则#xff1a;3. volatile变量规则#xff1a;4. 传递性#xff1a;5. start()规则#xff1a;6. join()规则#xff1a; 一、happens-before的定义
如果一个操作hap… 文章目录 一、happens-before的定义二、happens-before的规则1. 程序顺序规则2. 监视器锁规则3. volatile变量规则4. 传递性5. start()规则6. join()规则 一、happens-before的定义
如果一个操作happens-before另一个操作那么第一个操作的执行结果将对第二个操作可见而且第一个操作的执行顺序排在第二个操作之前。两个操作之间存在happens-before关系并不意味着Java平台的具体实现必须要按照 happens-before关系指定的顺序来执行。如果重排序之后的执行结果与按happens-before关系来执行的结果一致那么这种重排序并不非法
二、happens-before的规则
1. 程序顺序规则
一个线程中的每个操作happens-before于该线程中的任意后续操作。
程序顺序规则Program Order Rule指的是在单个线程内按照程序的顺序前面的操作 happens-before 后面的操作。这意味着一个线程中的每个操作都会在该线程中的任意后续操作之前发生。
下面是一个简单的Java程序示例展示了程序顺序规则的应用
public class ProgramOrderDemo {private int count 0;public void increment() {count; // 操作1}public void printCount() {System.out.println(Count: count); // 操作2}public static void main(String[] args) {ProgramOrderDemo demo new ProgramOrderDemo();Thread thread1 new Thread(() - {demo.increment(); // 线程1中的操作1demo.printCount(); // 线程1中的操作2});Thread thread2 new Thread(() - {demo.increment(); // 线程2中的操作1demo.printCount(); // 线程2中的操作2});thread1.start();thread2.start();}
}在上述示例中我们创建了两个线程thread1和thread2它们都会调用ProgramOrderDemo类中的increment()和printCount()方法。
根据程序顺序规则线程1中的操作1counthappens-before 线程1中的操作2System.out.println(Count: count)同样线程2中的操作1counthappens-before 线程2中的操作2System.out.println(Count: count)。
这意味着在每个线程中count操作一定会在System.out.println(Count: count)操作之前发生。因此我们可以确保在每个线程中打印的count值是递增的。
请注意尽管两个线程并行执行但由于程序顺序规则的存在各个线程内部的操作顺序是有序的不会出现线程间的竞争条件。
2. 监视器锁规则
对一个锁的解锁happens-before于随后对这个锁的加锁。
监视器锁规则Monitor Lock Rule指的是对一个锁的解锁操作 happens-before 于随后对同一个锁的加锁操作。这个规则确保了在多线程环境下对共享资源的同步访问。
下面是一个简单的Java程序示例展示了监视器锁规则的应用
public class MonitorLockDemo {private int count 0;private Object lock new Object();public void increment() {synchronized (lock) {count; // 线程1中的操作1}}public void printCount() {synchronized (lock) {System.out.println(Count: count); // 线程2中的操作2}}public static void main(String[] args) {MonitorLockDemo demo new MonitorLockDemo();Thread thread1 new Thread(() - {demo.increment(); // 线程1中的操作1});Thread thread2 new Thread(() - {demo.printCount(); // 线程2中的操作2});thread1.start();thread2.start();}
}在上述示例中我们创建了两个线程thread1和thread2它们都会调用MonitorLockDemo类中的increment()和printCount()方法。在这个类中我们使用了一个对象lock作为锁。
根据监视器锁规则线程1中的操作1counthappens-before 线程2中的操作2System.out.println(Count: count)。
这意味着在线程1中对锁的解锁操作一定会在线程2中对同一个锁的加锁操作之前发生。因此我们可以确保在执行打印操作时count的值是线程1已经更新过的。
请注意通过使用synchronized关键字和共享的锁对象我们确保了对count的访问是同步的避免了竞态条件和数据不一致的问题。这是因为监视器锁规则确保了解锁操作 happens-before 加锁操作从而确保了对共享资源的正确同步访问。
3. volatile变量规则
对一个volatile域的写happens-before于任意后续对这个volatile域的读。
volatile变量规则Volatile Variable Rule指的是对一个volatile域的写操作 happens-before 于任意后续对这个volatile域的读操作。这个规则确保了在多线程环境下对volatile变量的可见性和顺序性。
下面是一个简单的Java程序示例展示了volatile变量规则的应用
public class VolatileVariableDemo {private volatile int number 0;public void writeNumber() {number 42; // 写操作}public void readNumber() {System.out.println(Number: number); // 读操作}public static void main(String[] args) {VolatileVariableDemo demo new VolatileVariableDemo();Thread thread1 new Thread(() - {demo.writeNumber(); // 写操作});Thread thread2 new Thread(() - {demo.readNumber(); // 读操作});thread1.start();thread2.start();}
}在上述示例中我们创建了两个线程thread1和thread2它们都会调用VolatileVariableDemo类中的writeNumber()和readNumber()方法。在这个类中我们使用了一个volatile修饰的变量number。
根据volatile变量规则线程1中的写操作number 42happens-before 线程2中的读操作System.out.println(Number: number)。
这意味着在线程1中对number的写操作一定会在线程2中对同一个number的读操作之前发生。因此我们可以确保在执行打印操作时读取到的number的值是线程1已经写入的。
请注意使用volatile修饰变量可以保证其在多线程环境下的可见性和顺序性。这意味着对volatile变量的写操作对其他线程是可见的并且读操作一定会读取到最新的值。这是因为volatile变量规则确保了对volatile变量的写 happens-before 于任意后续对这个volatile变量的读。
4. 传递性
如果A happens-before B且B happens-before C那么A happens-before C。
下面是一个新的示例展示了happens-before关系的传递性
public class TransitivityDemo {private int number 0;private volatile boolean ready false;public void writer() {number 42; // 写操作ready true; // 写操作}public void reader() {if (ready) { // 读操作System.out.println(Number: number); // 读操作}}public static void main(String[] args) {TransitivityDemo demo new TransitivityDemo();Thread thread1 new Thread(() - {demo.writer(); // 写操作});Thread thread2 new Thread(() - {demo.reader(); // 读操作});thread1.start();thread2.start();}
}在这个示例中我们有一个TransitivityDemo类其中包含一个number变量和一个ready变量。在writer()方法中我们首先进行写操作number 42然后进行写操作ready true。在reader()方法中我们进行读操作if (ready)如果ready为true则进行读操作System.out.println(Number: number)。
根据happens-before关系的传递性线程1中的写操作number 42 happens-before 线程1中的写操作ready true。同时线程1中的写操作ready true happens-before 线程2中的读操作if (ready)。因此我们可以得出结论线程1中的写操作number 42 happens-before 线程2中的读操作if (ready)。
由于happens-before关系的传递性我们可以得出A happens-before C的结论即线程1中的写操作number 42 happens-before 线程2中的读操作System.out.println(Number: number)。
这个示例演示了happens-before关系的传递性其中A happens-before BB happens-before C因此A happens-before C。
5. start()规则
如果线程A执行操作ThreadB.start()启动线程B那么A线程的 ThreadB.start()操作happens-before于线程B中的任意操作。
当线程A执行ThreadB.start()方法启动线程B时根据Java内存模型Java Memory ModelJMM中的start()规则线程A的ThreadB.start()操作将happens-before于线程B中的任意操作。这意味着线程B中的任意操作都可以看到线程A在调用ThreadB.start()之前的操作。
下面是一个简单的示例代码来展示这个规则
public class ThreadDemo {private static boolean ready false;public static void main(String[] args) throws InterruptedException {ThreadB threadB new ThreadB();Thread threadA new Thread(() - {System.out.println(Thread A is doing some work);ready true;threadB.start(); // 线程A启动线程B});threadA.start(); // 启动线程AthreadA.join(); // 等待线程A执行完毕System.out.println(Thread B is ready? threadB.isReady());}static class ThreadB extends Thread {public boolean isReady() {return ready;}Overridepublic void run() {System.out.println(Thread B is running);// 在这里可以看到线程A在调用ThreadB.start()之前的操作System.out.println(Thread B sees ready? ready);}}
}在这个示例中线程A首先会执行一些工作并将ready属性设置为true。然后线程A调用threadB.start()来启动线程B。在线程B的run()方法中我们可以看到线程A在调用ThreadB.start()之前的操作即输出Thread B sees ready? true。
因此根据start()规则线程A的ThreadB.start()操作happens-before于线程B中的任意操作确保了对ready属性的修改对线程B可见。
运行结果如下
6. join()规则
如果线程A执行操作ThreadB.join()并成功返回那么线程B中的任意操作 happens-before于线程A从ThreadB.join()操作成功返回。
根据Java内存模型Java Memory ModelJMM中的join()规则如果线程A执行操作ThreadB.join()并成功返回那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。这意味着线程A能够看到线程B在join()之前的操作。
下面是一个简单的示例代码来展示这个规则
public class ThreadDemo {private static boolean ready false;public static void main(String[] args) throws InterruptedException {ThreadB threadB new ThreadB();Thread threadA new Thread(() - {System.out.println(Thread A is doing some work);threadB.start(); // 线程A启动线程Btry {threadB.join(); // 线程A等待线程B执行完毕} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread A sees ready? ready);});threadA.start(); // 启动线程AthreadA.join(); // 等待线程A执行完毕System.out.println(Thread B is ready? threadB.isReady());}static class ThreadB extends Thread {public boolean isReady() {return ready;}Overridepublic void run() {System.out.println(Thread B is running);ready true; // 在这里修改ready属性}}
}在这个示例中线程A首先会执行一些工作并启动线程B。然后线程A调用threadB.join()来等待线程B执行完毕。在线程B的run()方法中我们将ready属性设置为true。
当线程A从threadB.join()成功返回后它能够看到线程B在join()之前的操作。因此线程A输出的Thread A sees ready? true将显示线程B在join()之前将ready属性设置为true。
因此根据join()规则线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回确保了对ready属性的修改对线程A可见。
运行结果