标签:this system nts rup 更改 ice param 对象 内存屏障
可见性,原子性,有序性
Java内存模型(Java Memory Model)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。
每个线程都有自己独立的工作内存,里面保持该线程使用到的变量副本。

线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中进行读写。
不同线程之间无法直接访问其他线程工作内存的变量,所以线程间变量值的传递需要通过主内存来完成。
要实现共享变量的可见性,必须保证两点:
JMM关于synchronized的两条规定:
public class SyncExample {
    //修饰代码快
    public void test1(String name){
        synchronized (this){
            for (int i = 0; i < 10; i++){
                System.out.println(name + ":" + i);
            }
        }
    }
    //修饰方法
    public synchronized void test2(String name){
        for (int i = 0; i < 10; i++){
            System.out.println(name + ":" +i);
        }
    }
    public static void main(String[] args) {
        final SyncExample sync1 = new SyncExample();
        SyncExample sync2 = new SyncExample();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> sync1.test1("sync1"));
        executorService.execute(() -> sync2.test1("sync2"));
    }
}
//结果
sync2:0
sync1:0
sync2:1
sync1:1
sync2:2
sync1:2
sync1:3
sync2:3
sync1:4
sync2:4
sync1:5
sync2:5
sync1:6
sync2:6
sync1:7
sync2:7
sync1:8
sync2:8
sync1:9
sync2:9sync1和sync2的test1交替执行,说明同步代码块作用在当前对象,不同对象调用互不影响。
当换成test2()时,结果和上相同,说明synchronized修饰一个方法时,作用和同步代码块相同,都是作用当前对象。
所以,如果一个方法中是一个完整的同步代码快,它和synchronized修饰一个方法是等同的。
当修饰一个类时
  public static void test3(){
        synchronized (SyncExample.class){
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread() + "test3 :" + i + " ");
            }
        }
    }
Thread[pool-1-thread-1,5,main]test3 :0 
Thread[pool-1-thread-1,5,main]test3 :1 
Thread[pool-1-thread-1,5,main]test3 :2 
Thread[pool-1-thread-1,5,main]test3 :3 
Thread[pool-1-thread-1,5,main]test3 :4 
Thread[pool-1-thread-2,5,main]test3 :0 
Thread[pool-1-thread-2,5,main]test3 :1 
Thread[pool-1-thread-2,5,main]test3 :2 
Thread[pool-1-thread-2,5,main]test3 :3 
Thread[pool-1-thread-2,5,main]test3 :4 从结果上可以看出,修饰一个类时,作用于这个类的所有对象。
volatile通过加入内存屏障和禁止重排序优化来实现可见性
volatile变量在每次被线程访问时,都强迫从主内存中读取该变量的值,而当发生变化时,会强迫线程将最新的值刷新到主内存。
private int number = 0;
number ++;number ++ 不是原子操作,可以分为三步
1.读取number的值,2.将number值+1,3.写入最新的numnber的值
public class TestAtomicDemo {
    public static void main(String[] args) {
        AtomicDemo demo = new AtomicDemo();
        for (int i = 0; i < 10; i ++){
            new Thread(demo).start();
        }
    }
}
class AtomicDemo implements Runnable{
    private volatile int serialNumber = 0;
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() +":"+getSerialNumber());
    }
    public int getSerialNumber(){
        return serialNumber ++;
    }
}
Thread-0:0
Thread-1:1
Thread-3:3
Thread-2:2
Thread-5:4
Thread-4:5
Thread-6:7
Thread-7:7
Thread-8:6
Thread-9:8
可以看到Thread6和Thread7的执行结果相同。
原子性:提供了互斥访问,同一时刻,只能由一个线程对它进行操作。
jdk1.5后java.util.concurrent.atomic包下面提供可常用的原子变量。CAS(Compare-And-Swap)算法保证数据原子性。
private volatile AtomicInteger serialNumber = new AtomicInteger(0);
 public int getSerialNumber(){
        return serialNumber.incrementAndGet();
    }AtomicInteger的incrementAndGet()方法是如何保证原子性的呢?
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
    private volatile int value;
    /**
     * Atomically increments the current value,
     * with memory effects as specified by {@link VarHandle#getAndAdd}.
     *
     * <p>Equivalent to {@code addAndGet(1)}.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }从源码(我的是JDK10)中可以看到AtomicInteger使用了一个Unsafe类的getAndAddInt方法。
/**
     * Atomically adds the given value to the current value of a field
     * or array element within the given object {@code o}
     * at the given {@code offset}.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param delta the value to add
     * @return the previous value
     * @since 1.8
     */
    @HotSpotIntrinsicCandidate
    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!weakCompareAndSetInt(o, offset, v, v + delta));
        return v;
    }getIntVolatile方法,如果没有其他线程来处理Object o,这个方法返回只v就等于offset。
weakCompareAndSetInt方法就是CAS算法核心
offset为内存值,v是预估值,v+delta为更新值,当且仅当offset==v,才会+1操作。
Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序的过程不会影响单线程的执行结果,却会影响到多线程并发执行的正确性。
volatile, synchronized,Lock,可以保证有序性
##### happens-before原则
标签:this system nts rup 更改 ice param 对象 内存屏障
原文地址:https://www.cnblogs.com/Godfery/p/10353625.html