zoukankan      html  css  js  c++  java
  • 新手向同步关键字synchronized对this、class、object、方法的区别

    在看源代码时遇到多线程需要同步的时候,总是会看见几种写法,修饰方法、修饰静态方法、synchronized(Xxx.class)synchronized(this)synchronized(obj),之前一直没深究几种方式的区别,现在想来真是惊出一身冷汗,居然这个问题都没有仔细想清楚。

    synchronized的语义

    每个对象都有一个监视器monitor,被synchronized修饰,语义就是获取这个对象的monitor,反编译后可以看到monitorenter和monitorexit。synchronized关键字有三种应用方式(其实按标题来讲应该是5种,但是其中有两种都是与另外两种等价的):

    • 修饰实例方法
    • 修饰静态方法
    • 修饰代码块(指定对象)

    实验

    先上代码做实验验证一下

    public class SyncThread {
        private final Object lock = new Object();
    
        public void foo() throws Exception {
            synchronized (SyncThread.class) {
                for (int i = 0; i < 5; i++) {
                    System.out.println(">>>foo: " + i);
                    Thread.sleep(1000);
                }
            }
        }
    
        public void bar() throws Exception {
            synchronized (this) {
                for (int i = 0; i < 5; i++) {
                    System.out.println("<<<bar: " + i);
                    Thread.sleep(1000);
                }
            }
        }
    
        public void cpp() throws Exception {
            synchronized (lock) {
                for (int i = 0; i < 5; i++) {
                    System.out.println("===cpp: " + i);
                    Thread.sleep(1000);
                }
            }
        }
    
        public void der() throws Exception {
            for (int i = 0; i < 5; i++) {
                System.out.println("!!!der: " + i);
                Thread.sleep(1000);
            }
        }
    }
    

    可以看到有四种不同的synchronized修饰,以及一个没有同步的方法,再运行一下看看结果

    public class ThreadApp {
    
        public static void main(String[] args) {
            final SyncThread syncThread = new SyncThread();
            new Thread(() -> {
                try {
                    syncThread.foo();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
            new Thread(() -> {
                try {
                    syncThread.bar();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
            new Thread(() -> {
                try {
                    syncThread.cpp();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
            new Thread(() -> {
                try {
                    syncThread.der();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
    

    可以看到结果

    >>>foo: 0
    <<<bar: 0
    ===cpp: 0
    !!!der: 0
    >>>foo: 1
    ===cpp: 1
    <<<bar: 1
    !!!der: 1
    >>>foo: 2
    !!!der: 2
    <<<bar: 2
    ===cpp: 2
    >>>foo: 3
    <<<bar: 3
    ===cpp: 3
    !!!der: 3
    >>>foo: 4
    !!!der: 4
    <<<bar: 4
    ===cpp: 4
    

    分析

    从以上结果来看各线程并没有发生竞争,互不影响,其实明白了synchronized语义也很好理解以上结果,几个synchronized获取的monitor都不是一个,当然相互不影响。
    但是值得注意的几点:

    • synchronized(Xxx.class)获取的是类的monitor,所以与public synchronized static void some()修饰静态方法是等价的
    • synchronized(this)获取的是当前实例的monitor,所以与public synchronized void some()修饰实例方法是等价的

    以上两点可以通过修改上述代码中方法可以很容易验证,我们修改最后一个方法

    public synchronized static void der() throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println("!!!der: " + i);
            Thread.sleep(1000);
        }
    }
    

    运行得到结果

    !!!der: 0
    !!!der: 1
    !!!der: 2
    !!!der: 3
    !!!der: 4
    >>>foo: 0
    >>>foo: 1
    >>>foo: 2
    >>>foo: 3
    >>>foo: 4
    

    可以看到,确实synchronized(Xxx.class)synchronized修饰静态方法是等价的。再修改为synchronized修饰实例方法

    public synchronized void der() throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println("!!!der: " + i);
            Thread.sleep(1000);
        }
    }
    

    运行查看结果

    <<<bar: 0
    <<<bar: 1
    <<<bar: 2
    <<<bar: 3
    <<<bar: 4
    !!!der: 0
    !!!der: 1
    !!!der: 2
    !!!der: 3
    !!!der: 4
    

    可以看到,确实synchronized(this)synchronized修饰实例方法是等价的。
    总之,个人认为要理解几种不一样的地方,关键是理解清楚是获取的谁的monitor,只要是同一个monitor,当然就会发生同步!



    许多年前 你有一双清澈的双眼

    奔跑起来 像是一道春天的闪电

    想看遍这世界 去最遥远的远方

    感觉有双翅膀 能飞越高山和海洋




  • 相关阅读:
    用友软件T3出纳通提示单据锁定
    保存单据时提示“计量单位组不正确”
    cxgrid导出数据的格式设置(转载)
    会话打印机不会自动删除解决方法
    用友U8总账对账不平问题总结
    存货核算期初无法从库存取数
    SQL Server 2000 数据库日志太大!如何管理,清除,变小,压缩它
    Delphi控件cxGrid 如何动态创建列?
    关于Treeview 选中节点高亮有关问题
    U8远程接入客户端重新安装问题
  • 原文地址:https://www.cnblogs.com/1024Community/p/8711608.html
Copyright © 2011-2022 走看看