java线程锁synchronized的几种情况

一、代码块锁

   private final static SimpleDateFormat  SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

   private final static ExecutorService POOL = Executors.newFixedThreadPool(4);

        同一个类两个不同的方法,代码块加锁。

1、同一个对象锁
public class CaseSeven {

    private  final Object  lockA  = new Object();

    public  void   m1(){
        synchronized (lockA){
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+":------------>m1");
        }
    }

    public  void   m2(){
        synchronized (lockA){
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+":------------>m2");
        }
    }

}
     System.out.println(SDF.format(new Date())+" " + "------------>start");
     CaseSeven caseSeven = new CaseSeven();
     POOL.execute(caseSeven::m1);
     POOL.execute(caseSeven::m2);

         通过执行结果可以得出以下结论:

        同一对象的不同代码块,如果使用同一个对象锁,会产生竞态关系,先抢到锁的先执行。

2、不同对象锁
public class CaseSeven {

    private  final Object  lockA  = new Object();

    private  final Object  lockB  = new Object();

    public  void   m1(){
        synchronized (lockA){
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+":------------>m1");
        }
    }

    public  void   m2(){
        synchronized (lockB){
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+":------------>m2");
        }
    }

}
     System.out.println(SDF.format(new Date())+" " + "------------>start");
     CaseSeven caseSeven = new CaseSeven();
     POOL.execute(caseSeven::m1);
     POOL.execute(caseSeven::m2);

         通过执行结果可以看到,m1和m2不存是并行执行的:

        不同的代码区块,使用不同的对象锁,不存在竞态关系。

二、对象方法锁

1、成员方法加锁

         同一个对象成员方法有3个synchronized修饰的方法,通过睡眠模拟业务操作

    public class   CaseOne {

        public   synchronized   void  m1(){
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+"------------>m1");
        }

        public   synchronized   void  m2()  {
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+"------------>m2");
        }

        public   synchronized   void  m3()  {
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+"------------>m3");
        }
    }
       CaseOne caseOne = new CaseOne();
       System.out.println(SDF.format(new Date())+"------------>start");
       POOL.execute(caseOne::m1);
       POOL.execute(caseOne::m2);
       POOL.execute(caseOne::m3);

        运行结果:

        通过运行结果可以看出,三个业务方法,执行完成总共花费了6s,虽然使用了多线程,这三三个方法其实是串行作业的,因此可得出一下结论:   

        同一个对象的不同方法加synchronized修饰,只要其中一个方法抢到锁,其他被synchronized修饰的方法都互斥,本质是对象上锁。

2、成员方法不加锁
    public class   CaseTwo {

        public   synchronized   void  m1(){
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+"------------>m1");
        }

        public    void  m2()  {
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+"------------>m2");
        }

        public   synchronized   void  m3()  {
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+"------------>m3");
        }
    }
        CaseTwo caseTwo = new CaseTwo();
        System.out.println(SDF.format(new Date())+"------------>start");
        POOL.execute(caseTwo::m1);
        POOL.execute(caseTwo::m2);
        POOL.execute(caseTwo::m3);

          运行结果:

      三个业务方法执行完成总共花费了4s,结合第一个案例可以得出以下结论:

      同一个对象的不同方法加synchronized,其中加锁方法和不加锁的成员方法没有竞争关系,不产生互斥。

3、不同对象方法锁

        案例1和案例2研究的是同一个的对象,加锁和不加锁的情况,下面来研究不同对象的情况。

    public static class   CaseThree {

        private final String   name;

        public CaseThree(String name) {
            this.name = name;
        }

        public   synchronized   void  m1(){
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+  " "+this.name +"------------>m1");
        }
    }
        CaseThree caseThree1 = new CaseThree("caseThree1");
        CaseThree caseThree2 = new CaseThree("caseThree2");
        System.out.println(SDF.format(new Date())+" "+"------------>start");
        POOL.execute(caseThree1::m1);
        POOL.execute(caseThree2::m1);

         运行结果:

      由执行结果可以看出,对象caseThree1 和对象caseThree12调用同一个方法,同时执行完成,可以得出以下结论:

       不同的对象执行同一个成员方法,没有竞争关系,加锁方法不互斥。

三、类方法加锁

         下面研究一下一个类型static修饰的静态方法加锁

1、静态方法锁
    public static class   CaseFour {

        public  static synchronized   void  m1(){
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+  " "+"------------>m1");
        }

        public  static synchronized   void  m2(){
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+  " "+"------------>m2");
        }
    }
    
        System.out.println(SDF.format(new Date())+" "+"------------>start");
        POOL.execute(CaseFour::m1);
        POOL.execute(CaseFour::m2);

  

           由执行的结果可以得出以下结论:

           同一个类的不同静态方法之间存在竞争关系,先抢到锁的先执行。

2、不同对象静态方法锁
    public static class   CaseFive {

        public  static synchronized   void  m1(String name){
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+  " "+name+"------------>m1");
        }
    }
        CaseFive caseFive1 = new CaseFive();
        CaseFive caseFive2 = new CaseFive();
        System.out.println(SDF.format(new Date())+" "+"------------>start");
        POOL.execute(()->caseFive1.m1("caseFive1"));
        POOL.execute(()->caseFive2.m1("caseFive2"));

         由运行结果可以得到以下结论:

         同一个类的不同对象调用同一个静态方法,存在竞争关系,会产生互斥。

3、类方法锁和成员方法锁

   ◆场景一:

    public static class   CaseSix {

        public  static synchronized   void  m1(){
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+  " "+"------------>m1");
        }

        public  synchronized   void  m2(){
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
            System.out.println(SDF.format(new Date())+  " "+"------------>m2");
        }

    }
      CaseSix caseSix = new CaseSix();
      System.out.println(SDF.format(new Date())+" "+"------------>start");
      POOL.execute(()->caseSix.m1());
      POOL.execute(()->caseSix.m2());

        由执行结果可得出以下结论:

   

       同一个类的静态方法和成员方法加锁,同一个对象同时调用,会场生竞争关系,先抢到锁的先执行。

  ◆场景二:

       CaseSix caseSix1 = new CaseSix();
       CaseSix caseSix2 = new CaseSix();
       System.out.println(SDF.format(new Date())+" "+"------------>start");
       POOL.execute(()->caseSix1.m1());
       POOL.execute(()->caseSix2.m2());

   

         由运行结果可知:

        同一个类的不同对象,分别调用静态类和成员方法,不产生竞态关系。

四、线程锁总结

        通过查看反编译字节码(javap   -v  CaseOne.class),可以看到synchronized加锁机制是通过monitorenter加锁,通过

monitorexit自动释放锁。

          对象普通成员方法是通过ACC_SYNCHRONIZED标记加锁

         类方法加锁是通过, ACC_STATIC, ACC_SYNCHRONIZED进行标记的

       类方法是在类加载过程中已经打上标记了,类信息存储在jvm的常量池中,而对象的方法锁是在运行动态确定的,因此类方法锁和不同的对象成员方法锁之间不存在竞争关系。在并发情景,能用无锁的数据块就不要用锁,能锁区块,就不要锁整个方法体,能锁对象就不要用类锁,代码块锁、方法锁、类锁的锁粒度是主次增大的。

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值