java - 线程- synchronized

锁定的对象: 对象(Object) 和 类(Class)

对于jvm而言,实际锁定的是Object还是Class 是没有区别的,原因在于Class实际也是一个Object,唯一的不同点在于, Object对象是存在于Heap中,而Class是存在于MetaData(java8+)中;

对于这两种竞态条件而言,对于存在于MetaData中的class,每个jvm中最多只会存在一个,而对于堆中的Object,是可以人为操作的;对于堆中的静态条件也可以人为实现类似于MetaData中数据的竞态效果

 

   // 对于锁定 对象和类 的区别在于,其锁定的范围不同,对于锁定对象实际临界条件为当前对象,而当锁定类时,实际锁定的当前类
    // 对于 .class 实际也是一个Object 对象,因此实际并没有太大的区别
    public static void main(String[] args) {
        // 对象锁
        synchronized (new Object()) {
            // 对象存在于堆中

        }
        // 类锁
        synchronized (SynchronizedObjectAndClassDemo.class) {
            // 类存在于 metadata(java8+);
            //类存在于 perm(java7-)
        }
        Object o = SynchronizedObjectAndClassDemo.class;
    }

 

修饰范围:方法(method) 和 代码块(block)

  • 当修饰静态方法时,实际默认的竞态条件为当前类(.class)
  • 当修饰非静态方法时,实际默认的竞态条件为调用当前方法的对象实例(Object)
    // 锁定的静态条件是相同的
//    public synchronized static void echo(String message){
    public static void echo(String message) {
        synchronized (SynchronizedObjectAndClassDemo.class) {

        }
        System.out.println(message);
    }

    // 锁定的对象是相同的
    //    public synchronized void echoNoStatic(String message) { // 方法
    public void echoNoStatic(String message) {
        // 代码块
        synchronized (this) { // 对于当前的竞态条件为当前调用实例

        }
    }

 

重进入(reentrant)

重进入的限制条件为相同的线程以及相同的竞态条件,对于同一个线程对于同一个竞态条件已经在最外围获取到了所有权,则在内部再次遇到相同的竞态条件时,由于外部已经获取到了竞态条件所有权,因此在内部并不需要再次竞争,对于这种情况而言就属于重进入;在重进入操作时会对当前对象的monitor 进行累加操作(+1)来记录当前重进入次数

    public final static Object MONITOR_A = new Object();
    public final static Object MONITOR_B = new Object();

    public static void lockA(){
        synchronized (MONITOR_A){
            System.out.println("lockA");
            lockB();
        }
    }

    public static void lockB(){
        synchronized (MONITOR_B){
            System.out.println("lockB");
        }
    }

    public static void main(String[] args) {
        synchronized (MONITOR_B){ // 对于lockB而言的 MONITOR_B 实际就属于重进入,原因在于,在最外层以及得到了MONITOR_B 竞态条件的所有权,当在内部再次 使用 synchronized (MONITOR_B),由于线程在外部已经得到了竞态条件所有权,因此在内部再次使用时并不需要对竞态条件再次进行判断,直接进入;因此这种操作就属于重进入
            lockA();
        }

    }

 

Thread.sleep和 sync

        Object l1 = new Object();
        Object l2 = new Object();
        new Thread(() -> {
            synchronized (l1) {
                log.info("当前线程:{}持l1共享变量", Thread.currentThread().getId());
                try {
                    log.info("当前线程:{}准备休眠释放l1共享变量", Thread.currentThread().getId());
                    /**
                     * 在其注解中可以看到
                     * The thread does not lose ownership of any monitors.
                     * 对于sync 实际是利用的monitor ,因此当当前线程休眠时,并不会释放l1的所有权,因此另外一个线程还是被阻塞
                     */
                    Thread.sleep(10000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info("当前线程:{}休眠结束重新持有l1共享变量", Thread.currentThread().getId());
                synchronized (l2){
                    log.info("当前线程:{}持l2共享变量", Thread.currentThread().getId());
                }
                log.info("当前线程:{}释放l2共享变量", Thread.currentThread().getId());
            }
            log.info("当前线程:{}释放l1共享变量", Thread.currentThread().getId());
        }).start();

        new Thread(() -> {
            synchronized (l2) {
                log.info("当前线程:{}持l2共享变量", Thread.currentThread().getId());
                synchronized (l1){
                    log.info("当前线程:{}持l1共享变量", Thread.currentThread().getId());
                }
                log.info("当前线程:{}释放l1共享变量", Thread.currentThread().getId());
            }
            log.info("当前线程:{}释放l2共享变量", Thread.currentThread().getId());
        }).start();
        /**
         * 对于第一个线程首先持有了 l1 并未释放,去竞争l2发现第二个线程正在持有l2,此时当前线程就需要等待另外的线程释放l2
         * 第二个线程首先持有了 l2 并未释放,去竞争l1发现第一个线程正在持有l1,需要等待另外的线程释放l1
         * 在这种情况下,由于双方互相等待对方释放竞争资源,导致双方都获取不到资源
         */
    }

java.lang.Thread#sleep(long)

/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
*
* @param millis
* the length of time to sleep in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public static native void sleep(long millis) throws InterruptedException;



posted @ 2020-09-24 14:21  郭星  阅读(155)  评论(0)    收藏  举报