synchronized(this/.class/Object),synchronize方法区别

synchronized关键字的使用

  • 在日常开发中我们经常看到synchronized关键字,常见的几种应用是synchronized(this)、synchronized(class)与synchronized(Object),和synchronize方法,静态同步synchronized方法,这几种方式究竟有什么区别呢,当初学习java同步锁那块的时候好像就没怎么注意过,现在来通过实例比较下:
  • 对于synchronized(this):

      //核心业务代码
      public class HotProductService {
          public void serviceMethodA(){//业务A
              try {
                  synchronized (this) {
                      Log.d("HotProductService","A begin time="+System.currentTimeMillis());
                      Thread.sleep(2000);
                      Log.d("HotProductService","A end   time="+System.currentTimeMillis());
                  }
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
          public void serviceMethodB(){
              synchronized (this) {
                  Log.d("HotProductService","B begin time="+System.currentTimeMillis());
                  Log.d("HotProductService","B end   time="+System.currentTimeMillis());
              }
          }
      }
    
      //线程1
      public class ThreadA extends Thread{
          private HotProductService objectService;
          public ThreadA(HotProductService objectService){
              super();
              this.objectService=objectService;
          }
          @Override
          public void run() {
              super.run();//调用业务a
              objectService.serviceMethodA();
          }
      }
      //线程2
      public class ThreadB extends Thread {
          private HotProductService objectService;
          public ThreadB(HotProductService objectService){
              super();
              this.objectService=objectService;
          }
          @Override
          public void run() {
              super.run();//调用业务b
              objectService.serviceMethodB();
          }
      }
      //测试代码
      private void test1() {
              HotProductService service=new HotProductService();
              ThreadA a=new ThreadA(service);
              a.start();
              ThreadB b=new ThreadB(service);
              b.start();
          }
    

运行结果:

    02-24 10:05:12.135 6434-7477/com.example.refreshdemo D/HotProductService: A begin time=1519437912141
    02-24 10:05:14.135 6434-7477/com.example.refreshdemo D/HotProductService: A end   time=1519437914142
    02-24 10:05:14.135 6434-7478/com.example.refreshdemo D/HotProductService: B begin time=1519437914145
    02-24 10:05:14.135 6434-7478/com.example.refreshdemo D/HotProductService: B end   time=1519437914146

上面的结果我们能看到即使A业务耗时较长,B业务也并不会获得执行的机会,如果我们将核心业务代码中B业务或者A业务的同步锁去掉,其他照旧会是怎么样

    ...
    比如去掉B业务的同步锁
    public void serviceMethodB(){
    //synchronized (this) 
        {
        Log.d("HotProductService","B begin time="+System.currentTimeMillis());
        Log.d("HotProductService","B end   time="+System.currentTimeMillis());
        }
    }
    ...

运行结果:

    02-24 02:27:19.248 3562-3629/com.example.refreshdemo D/HotProductService: A begin time=1519439239248
    02-24 02:27:19.248 3562-3630/com.example.refreshdemo D/HotProductService: B begin time=1519439239248
    02-24 02:27:19.248 3562-3630/com.example.refreshdemo D/HotProductService: B end   time=1519439239248
    02-24 02:27:21.248 3562-3629/com.example.refreshdemo D/HotProductService: A end   time=1519439241248

结论:synchronized (this)使用的对象监视器是一个,即是该对象自身,当一个线程访问HotProductService的一个synchronized (this)同步代码块时,其它线程对同一个HotProductService中其它的synchronized (this)同步代码块的访问将是堵塞,实现了代码顺序的同步执行。

  • 对于synchronized修饰的方法:
    上面的结论对于synchronized修饰的方法是否同样有效呢,通过实例看下:

      //核心业务代码
      public class HotProductService {
          //业务A
          public synchronized void objectMethodA(){
              Log.d("HotProductService","run----objectMethodA2");
          }
          //业务B
          public void objectMethodB(){
              synchronized (this) {
                  try {
                      for (int i = 1; i <= 10; i++) {
                          Log.d("HotProductService","synchronized thread name:"+Thread.currentThread().getName()+"-->i="+i);
                          Thread.sleep(1000);
                      }
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      
      //A线程
      public class ThreadA2 extends Thread{
          private HotProductService objectService;
          public ThreadA2(HotProductService objectService){
              super();
              this.objectService=objectService;
          }
          @Override
          public void run() {
              super.run();
              objectService.objectMethodA();
          }
      }
    
      //B线程
      public class ThreadB2 extends Thread {
          private HotProductService objectService;
          public ThreadB2(HotProductService objectService){
              super();
              this.objectService=objectService;
          }
          @Override
          public void run() {
              super.run();
              objectService.objectMethodB();
          }
      }
      
      private void test2() {
              HotProductService service=new HotProductService();
              ThreadA2 a=new ThreadA2(service);
              a.start();
              ThreadB2 b=new ThreadB2(service);
              b.start();
          }
    

运行结果:

    02-24 02:48:32.212 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=1
    02-24 02:48:33.213 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=2
    02-24 02:48:34.214 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=3
    02-24 02:48:35.215 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=4
    02-24 02:48:36.216 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=5
    02-24 02:48:37.217 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=6
    02-24 02:48:38.218 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=7
    02-24 02:48:39.219 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=8
    02-24 02:48:40.220 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=9
    02-24 02:48:41.221 3562-3765/com.example.refreshdemo D/HotProductService: synchronized thread name:Thread-166-->i=10
    02-24 02:48:42.223 3562-3764/com.example.refreshdemo D/HotProductService: run----objectMethodA2

同样验证了只能同时被一个线程所访问synchronized代码块。其他线程遇到synchronized代码块将会堵塞。

  • 那么synchronized(Object)呢

      //核心业务代码
          public class HotProductService {
              private String uname;
              private String pwd;
              String lock=new String();
              public void setUserNamePassWord(String userName,String passWord){
                  try {
                      synchronized (lock) {
                          Log.d("HotProductService","thread name=" + Thread.currentThread().getName()
                                  + " 进入代码快:" + System.currentTimeMillis());
                          uname=userName;
                          Thread.sleep(3000);
                          pwd=passWord;
                          Log.d("HotProductService","thread name="+Thread.currentThread().getName()
                                  +" 进入代码快:"+System.currentTimeMillis()+"入参uname:"+uname+"入参pwd:"+pwd);
                      }
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }
    
      //A线程
      public class ThreadA3 extends Thread{
          private HotProductService objectService;
          public ThreadA3(HotProductService objectService){
              super();
              this.objectService=objectService;
          }
          @Override
          public void run() {
              super.run();
              objectService.setUserNamePassWord("a","aa");
          }
      }
    
      //B线程
      public class ThreadB3 extends Thread {
          private HotProductService objectService;
          public ThreadB3(HotProductService objectService){
              super();
              this.objectService=objectService;
          }
          @Override
          public void run() {
              super.run();
              objectService.setUserNamePassWord("b","bb");
          }
      }
    
      HotProductService service=new HotProductService();
      ThreadA2 a=new ThreadA2(service);
      a.start();
      ThreadB2 b=new ThreadB2(service);
      b.start();
    

运行结果:

    02-24 02:57:41.380 3975-4016/? D/HotProductService: thread name=A 进入代码快:1519441061380
    02-24 02:57:44.382 3975-4016/com.example.refreshdemo D/HotProductService: thread name=A 进入代码快:1519441064382入参uname:a入参pwd:aa
    02-24 02:57:44.382 3975-4017/com.example.refreshdemo D/HotProductService: thread name=B 进入代码快:1519441064382
    02-24 02:57:47.383 3975-4017/com.example.refreshdemo D/HotProductService: thread name=B 进入代码快:1519441067383入参uname:b入参pwd:bb

如果我们将

    //核心业务代码
        public class HotProductService {
            private String uname;
            private String pwd;
           //String lock=new String();移入方法会得到什么样的结果呢
            public void setUserNamePassWord(String userName,String passWord){
                try {
                    String lock=new String();
                    synchronized (lock) {
                        Log.d("HotProductService","thread name=" + Thread.currentThread().getName()
                                + " 进入代码快:" + System.currentTimeMillis());
                        uname=userName;
                        Thread.sleep(3000);
                        pwd=passWord;
                        Log.d("HotProductService","thread name="+Thread.currentThread().getName()
                                +" 进入代码快:"+System.currentTimeMillis()+"入参uname:"+uname+"入参pwd:"+pwd);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

其他代码照旧得到
运行结果:

    02-24 03:04:16.757 4148-4187/? D/HotProductService: thread name=A 进入代码快:1519441456757
    02-24 03:04:16.758 4148-4188/? D/HotProductService: thread name=B 进入代码快:1519441456758
    02-24 03:04:19.758 4148-4187/com.example.refreshdemo D/HotProductService: thread name=A 进入代码快:1519441459758入参uname:b入参pwd:aa
    02-24 03:04:19.759 4148-4188/com.example.refreshdemo D/HotProductService: thread name=B 进入代码快:1519441459759入参uname:b入参pwd:bb

是不是很奇怪出错了?为什么呢 移入方法内部就不对了,比较前后改变,发现修改后每次调用方法时(哪个线程调用方法时)都会重新new一个对象作为对象锁,但是修改前不管哪次进入方法时(哪个线程调用方法时)用的都是同一个对象,所以我们得到结论:

使用自定义任意对象进行同步锁 不同线程必须为同一对象,否则仍旧是异步运行的

  • synchronized(.class)与静态同步synchronized方法

可以直接给出的结论是这两种方式的同步是一样的,使用的同步锁都是对应的类作为对象锁,在jvm中类是唯一的,那也就是说对任何对象都是同步的,因为具有唯一的对象锁。下面来验证下:

    //核心业务代码
    public class HotProductService {
        //业务A
        public  void methodA(){
            try {
                synchronized (HotProductService.class){
                    Log.d("HotProductService","static methodA begin Name:" + Thread.currentThread().getName() + " times:" + System.currentTimeMillis());
                    Thread.sleep(2000);
                    Log.d("HotProductService","static methodA end   Name:"+Thread.currentThread().getName()+" times:"+System.currentTimeMillis());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //业务B
        public  void methodB(){
            synchronized (HotProductService.class){
                Log.d("HotProductService","static methodB begin Name:"+Thread.currentThread().getName()+" times:"+System.currentTimeMillis());
                Log.d("HotProductService","static methodB end   Name:"+Thread.currentThread().getName()+" times:"+System.currentTimeMillis());
            }
        }
    }

    //A线程
    public class ThreadA5 extends Thread{
    
        private  HotProductService mHotProductService;
    
        public ThreadA5(HotProductService hotProductService){
            mHotProductService = hotProductService;
        }
        @Override
        public void run() {
            super.run();
            mHotProductService.methodA();
        }
    }
    
    //B线程
    public class ThreadB5 extends Thread {
    
        private  HotProductService mHotProductService;
    
        public ThreadB5(HotProductService hotProductService){
            mHotProductService = hotProductService;
        }
        @Override
        public void run() {
            super.run();
            mHotProductService.methodB();
        }
    }

    private void test5() {
        HotProductService service= new HotProductService();
        ThreadA5 a=new ThreadA5(service);
        a.start();
        ThreadB5 b=new ThreadB5(service);
        b.start();
    }

运行结果:

    02-24 03:24:11.250 4148-4282/com.example.refreshdemo D/HotProductService: static methodA begin Name:Thread-174 times:1519442651250
    02-24 03:24:13.252 4148-4282/com.example.refreshdemo D/HotProductService: static methodA end   Name:Thread-174 times:1519442653252
    02-24 03:24:13.252 4148-4283/com.example.refreshdemo D/HotProductService: static methodB begin Name:Thread-175 times:1519442653252
    02-24 03:24:13.252 4148-4283/com.example.refreshdemo D/HotProductService: static methodB end   Name:Thread-175 times:1519442653252

使用.class作为对象锁保证了不能线程的同步。
引用网上常见的说法就是:

synchronize修饰的方法和 synchronize(this) 都是锁住自己本身的对象 而synchronize(class) synchronize(object) 都是锁别的对象

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,444评论 1 15
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,952评论 1 18
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 今年的教师节和往年一样,毕业之后就和我们没啥太多关系,突然一位来自新疆老同学的电话,一寒暄在武汉呢,啥也别说...
    凡提阅读 430评论 0 1