java学习-JMM-synchronization-order

"The Java Language Specification Java SE 12 Edition"

17.4.4 Synchronization Order
Every execution has a synchronization order. A synchronization order is a total
order over all of the synchronization actions of an execution. For each thread t,
the synchronization order of the synchronization actions (§17.4.2) in t is consistent
with the program order (§17.4.3) of t.
Synchronization actions induce the synchronized-with relation on actions, defined
as follows:
• An unlock action on monitor m synchronizes-with all subsequent lock actions on
m (where "subsequent" is defined according to the synchronization order).
• A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent
reads of v by any thread (where "subsequent" is defined according to the
synchronization order).
• An action that starts a thread synchronizes-with the first action in the thread it
starts.THREADS AND LOCKS Memory Model 17.4
675
• The write of the default value (zero, false, or null) to each variable
synchronizes-with the first action in every thread.
Although it may seem a little strange to write a default value to a variable before the
object containing the variable is allocated, conceptually every object is created at the
start of the program with its default initialized values.
• The final action in a thread T1 synchronizes-with any action in another thread T2
that detects that T1 has terminated.
T2 may accomplish this by calling T1.isAlive() or T1.join().
• If thread T1 interrupts thread T2, the interrupt by T1 synchronizes-with any point
where any other thread (including T2) determines that T2 has been interrupted (by
having an InterruptedException thrown or by invoking Thread.interrupted
or Thread.isInterrupted).
The source of a synchronizes-with edge is called a release, and the destination is
called an acquire

 

对于加锁解锁同步关系的理解

 An unlock action on monitor m synchronizes-with all subsequent lock actions on m (where "subsequent" is defined according to the synchronization order).

    private static volatile int sharedData;
    private static final Object LOCK = new Object();

    public static void changeData(int data) {
        /**
         *
         * An unlock action on monitor m synchronizes-with all subsequent lock actions on
         * m (where "subsequent" is defined according to the synchronization order).
         * 对于同步关系中这句话的理解为:
         * 存在多个不同的竞争线程来准备持有LOCK; 此时T1获取到了锁, 而其他的线程处于等待LOCK monitor release中;
         * 此时 T1释放 LOCK, 此时其他的线程中只会有一个线程T2获取到锁;
         * 对于T1 unlock(LOCK) -> T2 lock(LOCK) 对于这两个关系实际是同步的; 这里实际并不会去考虑其他线程竞争当前资源的过程,对于上一个线程解锁和下一个线程加锁实际就是同步的
         *
         */
        synchronized (LOCK) {
            sharedData = data;
        }
    }

对于volatile 可见性 同步关系理解

A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order)

 

    /**
     * A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent
     * reads of v by any thread (where "subsequent" is defined according to the
     * synchronization order)
     * <p>
     * 对于我个人理解而言,volatile 和  mysql 可重复读是类似的, 对于并发线程中当存在读线程和写线程操作而言,当写线程执行过写操作后,当读线程再次执行读操作时实际是读取到的写线程修改后的数据,而非读线程中之前读操作得到的数据
     */
    private static volatile int volatileSharedData;

    // volatile 写
    public static void volatileChangeData(int data) {
        volatileSharedData = data;
    }

    // volatile 读
    public static int getVolatileSharedData() {
        return volatileSharedData;
    }

 对于线程启动和线程中的第一个操作同步性理解

对于Thread#start中创建物理线程称为线程启动,而线程启动后的第一个操作指的是,线程启动后通过回调来调用 Thread#run 称为线程启动后的第一个操作; 

An action that starts a thread synchronizes-with the first action in the thread it starts

对于Thread#start本身就是"synchronized"修饰表示了操作的同步性在同步操作中调用"java.lang.Thread#start0"方法来实现物理线程创建以及线程对象中指定的Runnable回调启动;

    public static void threadStart() throws InterruptedException {
        Thread thread = new Thread(() -> {
            log.info("线程启动后通过回调调用run方法");
        });
        thread.start();
        thread.join();
    }

 对于变量初始化赋值以及当线程第一次使用变量时,这些操作也是同步的

The write of the default value (zero, false, or null) to each variable
synchronizes-with the first action in every thread.
Although it may seem a little strange to write a default value to a variable before the
object containing the variable is allocated, conceptually every object is created at the
start of the program with its default initialized values.

对于这个操作而言,对象也只能在初始化后才能被使用;但对于一下引用对象而言,存在一些可变操作,因此不同线程读取到的数据可能是不同的;但并不可能读取到数据的中间状态

    @AllArgsConstructor
    @ToString
    public static class Person {
        private final String name;
        private final int age;
        private final Collection<Integer> tags;
    }

    public static void objectInit() throws InterruptedException {
        List<Integer> tags = Arrays.asList(1, 2, 3);
        /**
         * 对于person对象而言肯定是在创建成功后才可以使用,不可能读取到对象属性的中间状态;
         * 对于person对象而言,在读取到时,对象中的属性肯定已经被赋值结束
         * 对于 原生类型,不可变对象类型实际不可能读取到这些数据的不完整状态
         * 而对于一些可变对象(集合等)而言,对于异步线程操作而言实际是有可能读取到数据的中间状态
         */
        Person person = new Person("guoxing", 18, tags);
        Thread thread = new Thread(()->{
            log.info("{}",person);
        });
        thread.start();
        Thread.sleep(1000);
        // 将 3 -> 1
        tags.set(2,1);
        thread.join();
        log.info("{}",person);
    }

 对于线程终止状态同样是使用volatile保证了其可见性

If thread T1 interrupts thread T2, the interrupt by T1 synchronizes-with any point where any other thread (including T2) determines that T2 has been interrupted (by having an InterruptedException thrown or by invoking Thread.interrupted or Thread.isInterrupted)

    public static void threadInterrupt() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            if (Thread.interrupted()) {
                log.info("当前线程状态已被终止,且会清除当前线程的状态并清除相关的终止事件");
            }
        });

        // 终止T2线程
        Thread t2 = new Thread(() -> {
            t1.interrupt();
            log.info("终止t1线程");
        });
        Thread t3 = new Thread(() -> {
            if (t1.isInterrupted()) {
                log.info("t1线程已被终止");
            }
        });
        // 通过volatile来实现可见性
        // 当 t2 -> t1.interrupt -> t3 会读取到t1线程的interrupt状态
        t2.start();
        t3.start();
        t1.start();

        Thread.sleep(5000);

    }

 对于同步关系边界的定义

The source of a synchronizes-with edge is called a release, and the destination is called an acquire.

对于同步关系中 上一个线程释放共享资源是起点,下一个线程获取共享资源是终点 

posted @ 2020-10-12 10:16  郭星  阅读(140)  评论(0)    收藏  举报