欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Threads and Locks

程序员文章站 2022-07-12 17:02:00
...

转载自:https://javadoop.com/post/Threads-And-Locks-md
主要是对原文加上自己的理解与标注

  1. Thread.sleep 和 Thread.yield 都不具有同步的语义。在 Thread.sleep 和 Thread.yield 方法调用之前,不要求虚拟机将寄存器中的缓存刷出到共享内存中,同时也不要求虚拟机在这两个方法调用之后,重新从共享内存中读取数据到缓存。

  2. 调用thread的interrupt方法,会导致它从对象锁的等待集合中剔除,如果interrupt和notify同时调用,如果notify先执行,则线程正常返回同时中断状态为true,如果interrupt先执行则线程获取到监视器锁后,继续执行会报InterruptedException异常

  3. 对象的默认初始值 happens-before 于程序中对它的其他操作
    也就是说不管我们要对这个对象干什么,这个对象即使没有创建完成,它的各个属性也一定有初始零值。

  4. 如果 A 线程中调用了 B.join(),那么 B 线程中的操作先于 A 线程 join() 返回之后的任何语句。因为 join() 本身就是让其他线程先执行完的意思。

  5. 对象只有在构造方法结束了才被认为完全初始化了。如果一个对象完全初始化以后,一个线程持有该对象的引用,那么这个线程一定可以看到正确初始化的 final 属性的值。在对象的构造方法中设置 final 属性;同时在对象初始化完成前,不要将此对象的引用写入到其他线程可以访问到的地方(也就是在构造函数中,不要把this对象暴露出去)。如果这个条件满足,当其他线程看到这个对象的时候,那个线程始终可以看到正确初始化后的对象的 final 属性(这个也解释了双重检验锁中变量如果为final ,不存在变量没有初始化完成就暴露出去了,final 属性的写操作不会和此引用的赋值操作发生重排序)。

  6. 线程会因为以下原因从等待集合中剔除:

以下三种输出对第二点进行验证:
第一种输出

package com.tuniu.tmc.apaas.order.controller;

public class WaitNotify {
    volatile int a = 0;

    public static void main(String[] args) {

        Object object = new Object();

        WaitNotify waitNotify = new WaitNotify();

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {

                synchronized (object) {
                    System.out.println("线程1 获取到监视器锁");
                    try {
                        object.wait();
                        System.out.println("线程1 正常恢复啦。" + Thread.interrupted());
                    } catch (InterruptedException e) {
                        System.out.println("线程1 wait方法抛出了InterruptedException异常");
                    }
                }
            }
        }, "线程1");
        thread1.start();

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {

                synchronized (object) {
                    System.out.println("线程2 获取到监视器锁");
                    try {
                        object.wait();
                        System.out.println("线程2 正常恢复啦。");
                    } catch (InterruptedException e) {
                        System.out.println("线程2 wait方法抛出了InterruptedException异常");
                    }
                }
            }
        }, "线程2");
       // thread2.start();

        // 这里让 thread1 和 thread2 先起来,然后再起后面的 thread3
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程3 拿到了监视器锁。");
                    System.out.println("线程3 设置线程1中断");
                    thread1.interrupt(); // 1
                    waitNotify.a = 1; // 这行是为了禁止上下的两行中断和notify代码重排序

                    System.out.println("线程3,休息一会");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                    }

                    System.out.println("线程3 休息够了");
                    System.out.println("线程3 调用notify");
                    object.notify(); //2
                }
            }
        }, "线程3").start();
    }
}

输出结果:
线程1 获取到监视器锁
线程3 拿到了监视器锁。
线程3 设置线程1中断
线程3,休息一会
线程3 休息够了
线程3 调用notify
线程1 wait方法抛出了InterruptedException异常

第二种输出:

package com.tuniu.tmc.apaas.order.controller;

public class WaitNotify {
    volatile int a = 0;

    public static void main(String[] args) {

        Object object = new Object();

        WaitNotify waitNotify = new WaitNotify();

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {

                synchronized (object) {
                    System.out.println("线程1 获取到监视器锁");
                    try {
                        object.wait();
                        System.out.println("线程1 正常恢复啦。" + Thread.interrupted());
                    } catch (InterruptedException e) {
                        System.out.println("线程1 wait方法抛出了InterruptedException异常");
                    }
                }
            }
        }, "线程1");
        thread1.start();

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {

                synchronized (object) {
                    System.out.println("线程2 获取到监视器锁");
                    try {
                        object.wait();
                        System.out.println("线程2 正常恢复啦。");
                    } catch (InterruptedException e) {
                        System.out.println("线程2 wait方法抛出了InterruptedException异常");
                    }
                }
            }
        }, "线程2");
       // thread2.start();

        // 这里让 thread1 和 thread2 先起来,然后再起后面的 thread3
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程3 拿到了监视器锁。");
                    System.out.println("线程3 设置线程1中断");
                    thread1.interrupt(); // 1
                    waitNotify.a = 1; // 这行是为了禁止上下的两行中断和notify代码重排序
                    System.out.println("线程3 调用notify");
                    object.notify(); //2
                    System.out.println("线程3 调用完notify后,休息一会");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                    }
                    System.out.println("线程3 休息够了,结束同步代码块");
                }
            }
        }, "线程3").start();
    }
}
输出结果:
线程1 获取到监视器锁
线程3 拿到了监视器锁。
线程3 设置线程1中断
线程3 调用notify
线程3 调用完notify后,休息一会
线程3 休息够了,结束同步代码块
线程1 wait方法抛出了InterruptedException异常

或者
线程1 获取到监视器锁
线程3 拿到了监视器锁。
线程3 设置线程1中断
线程3 调用notify
线程3 调用完notify后,休息一会
线程3 休息够了,结束同步代码块
线程1 正常恢复啦。true

第三种输出:如果一个线程同时被中断和通知唤醒,同时这个线程通过抛出 InterruptedException 异常从 wait 中返回,那么等待集合中的某个其他线程一定会被通知。 也就是说等待集合中其它线程被选中,继续执行

/**
 * Created by hongjie on 2017/7/7.
 * 
 */
public class WaitNotify {

    volatile int a = 0;

    public static void main(String[] args) {

        Object object = new Object();

        WaitNotify waitNotify = new WaitNotify();

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {

                synchronized (object) {
                    System.out.println("线程1 获取到监视器锁");
                    try {
                        object.wait();
                        System.out.println("线程1 正常恢复啦。");
                    } catch (InterruptedException e) {
                        System.out.println("线程1 wait方法抛出了InterruptedException异常");
                    }
                }
            }
        }, "线程1");
        thread1.start();

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {

                synchronized (object) {
                    System.out.println("线程2 获取到监视器锁");
                    try {
                        object.wait();
                        System.out.println("线程2 正常恢复啦。");
                    } catch (InterruptedException e) {
                        System.out.println("线程2 wait方法抛出了InterruptedException异常");
                    }
                }
            }
        }, "线程2");
        thread2.start();

         // 这里让 thread1 和 thread2 先起来,然后再起后面的 thread3
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("线程3 拿到了监视器锁。");
                    System.out.println("线程3 设置线程1中断");
                    thread1.interrupt(); // 1
                    waitNotify.a = 1; // 这行是为了禁止上下的两行中断和notify代码重排序
                    System.out.println("线程3 调用notify");
                    object.notify(); //2
                    System.out.println("线程3 调用完notify后,休息一会");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                    }
                    System.out.println("线程3 休息够了,结束同步代码块");
                }
            }
        }, "线程3").start();
    }
}

// 最常见的output:
线程1 获取到监视器锁
线程2 获取到监视器锁
线程3 拿到了监视器锁。
线程3 设置线程1中断
线程3 调用notify
线程3 调用完notify后,休息一会
线程3 休息够了,结束同步代码块
线程2 正常恢复啦。
线程1 wait方法抛出了InterruptedException异常
相关标签: 并发