java并发编程的基础

来源:互联网 发布:塞内加 知乎 编辑:程序博客网 时间:2024/05/20 17:09

0 概述

要编写正确的并发程序,关键问题在于:在访问共享的可变状态时需要进行的正确的管理,即原子操作和内存可见性。我们知道同步代码块和同步方法可以确保原子操作,但是一种常见的误解是认为
synchronized只能用于实现原子操作;同步其实还有另一个重要方面:内存可见性。多线程的环境下,我们希望确保一个线程修改了状态之后对另一个线程是可见的。

1 内存可见性

加锁机制既可以保证可见性,又可以保证原子性,而volatile修饰变量只能保证可见性。

public class NoVisibility {    private static Test test = new Test();    static class Test {        private boolean ready = false;        private int number = 0;    }    private static void run() {        new Thread(new Runnable() {            @Override            public void run() {                while (!test.ready) {                    // System.out.println("number" + test.number);                    //Thread.yield();                }                System.out.println("number" + test.number);            }        }).start();    }    public static void main(String[] args) throws Exception {        run();        //确保子线程已经启动        Thread.sleep(1000);        test.number = 2;        test.ready = true;    }}

可以看到如果test是volatile的话线程可以正常结束。
但是如果test不是volatile的话,线程将很大可能一直无法结束,因为没有volatile修饰的话test.ready一直读的是缓存(线程中工作内存),而非主存值,除非cpu有空才会去重新获取test.ready的值,比如在循环体内释放CPU执行资格或者执行一个复杂操作new个对象sysout以下什么的。

2 先行发生原则

先行发生是java内存模型中定义的两项操之间的偏序关系,如果说操作A先行发生于B操作,其实就是说在发生B操作之前,操作A产生的影响能被B观察到,这个影响包括修改了内存共享变量、发送了消息、调用方法等。下面是java内存模型下一些“天然的”先行发生关系,这些先行发生关系无线任何同步器协助就已经存在,可以在编码中直接使用。如果两个操作之间不在此列,并且无法从下列规则推导出来的话,它们就没有顺序性保障,虚拟机就可以对它们随意的重排序。

  • 程序次序规则(Program Order Rule):在一个线程内,按照程序代码顺序,书写在前面的代码先行发生于书写后面的代码。
  • 管程锁定规则(Monitor Lock Rule)一个unlock 操作先行发生后面对同一个锁lock操作。
  • volatile 变量规则(Volatile Variable Rule)对一个volatile变量的写操作先行发生于后面这个变量的读操作。
  • 线程启动规则(Thread Start Rule)Thread对象的start() 方法先行发生于此线程的的每一个动作。
  • 线程终止规则(Thread Termination Rule)线程中的所有操作都先行发生于对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
  • 线程中断规则(Thread Interruption Rule)对线程Interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件发生,可以通过Thread.isInterrupted()方法检测到是否发生中断。
  • 对象终结规则(Finalizer rule)一个对象的初始化完成,先行发生于它的finalize()方法的开始。
  • 传递性(Transitivity)如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出A先行发生于操作C的结论。

深刻理解java内存模型先行发生关系,对于编写正确的并发程序有着很大指导意义。

参考文献
[1] 深入理解java 虚拟机(第二版),周志明著
[2]java 并发编程实战