JAVA面试题分享十一:Synchronized静态方法和普通方法锁的对象是什么?

一、前言

synchronized关键字,我们一般称之为”同步锁“,用它来修饰需要同步的方法和需要同步代码块,默认是当前对象作为锁的对象。在修饰类时(或者修饰静态方法),默认是当前类的Class对象作为所的对象故存在着方法锁、对象锁、类锁 这样的概念

1、普通同步方法,是实例锁,锁是当前实例对象。
不同的实例对象调用非静态同步方法不存在竞争关系,同一个实例对象调用非静态同步方法时存在竞争关系。注意这里是类的当前实例, 类的两个不同实例就没有这种约束了。
举例:线程A和线程B都用了同一个Student对象,Student对象有一个/多个非静态的同步方法,那么同一时刻只能有一个线程访问这些非静态同步方法,其他线程都不能访问所有的非静态的同步方法。

2、静态同步方法,是类锁,锁是当前类的Class对象。
调用静态同步方法和其他静态方法存在竞争关系,与非静态方法不存在竞争关系。
限制多线程中该类的所有实例同时访问该类所对应的静态同步方法。
举例:线程A用了Student1对象,线程B用了Student2对象,Student对象有一个/多个静态同步方法,那么同一时刻只能有一个线程访问这些静态同步方法,因为同一时刻只允许一个实例对象来访问,要么是Student1对象,要么是Student2对象,反正不能多个实例同时访问静态同步方法。
 

3、同步代码块,锁是Synchonized括号里配置的对象
synchronized(obj):obj称之为同步监视器,是一个Java对象

  • 同步监视器必须是引用数据类型,不能是基本数据类型。
  • 可以改变同步监视器堆中属性的值,但是不能改变指向堆中的地址。
  • 尽量不要用String和包装类型作为同步监视器,容易造成指向堆中地址的变化。
  • 一般使用共享资源作为同步监视器,也可以专门创建一个没有任何语义化含义的同步监视器。

同步方法中无需同步监视器;

因为同步方法的同步监视器就是this,即对象本身,或者说class(反射);

同步监视器执行过程:

  • 线程1访问,锁定同步监视器,执行代码;
  • 线程2访问,同步监视器已被锁定,无法执行代码块;
  • 线程1访问完毕,解锁同步监视器;
  • 线程2访问,同步监视器未被访问,线程2锁定同步监视器,执行代码。

synchronized(this):锁的是该方法所在类的实例对象
当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,其他线程对object中所有synchronized(this)同步代码块的访问将被阻塞。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。


ps:不管如何加锁,都是控制都这个方法或代码块的访问,而不是整个类,这点不要忘记了。

二、原理

Synchronized方法锁(也叫对象锁)

1.修饰在方法上,多个线程调用同一个对象的同步方法会阻塞,调用不同对象的同步方法不会阻塞。(java对象的内存地址是否相同)

public synchronized void obj3() {
           int i = 5;
           while (i-- > 0) {
               System.out.println(Thread.currentThread().getName() + " : " + i);
               try {
                   Thread.sleep(500);
               } catch (InterruptedException ie) {
               }
           }
   }

2.修饰代码块,这个this就是指当前对象(类的实例),多个线程调用同一个对象的同步方法会阻塞,调用不同对象的同步方法不会阻塞。(java对象的内存地址是否相同)

public void obj2() {
       synchronized (this) {
           int i = 5;
           while (i-- > 0) {
               System.out.println(Thread.currentThread().getName() + " : " + i);
               try {
                   Thread.sleep(500);
               } catch (InterruptedException ie) {
               }
           }
       }
   }

3.修饰代码块,这个str就是指String对象,多个线程调用同一个对象的同步方法会阻塞,调用不同对象的同步方法不会阻塞。(java对象的内存地址是否相同)

public void obj2() {
       String str=new String("lock");
//在方法体内,调用一次就实例化一次,多线程访问不会阻塞,因为不是同一个对象,锁是不同的
       synchronized (str) {
           int i = 5;
           while (i-- > 0) {
               System.out.println(Thread.currentThread().getName() + " : " + i);
               try {
                   Thread.sleep(500);
               } catch (InterruptedException ie) {
               }
           }
       }
   }

public static void main(String[] args) throws InterruptedException {
        test test=new test();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.obj2();
            }
        }).start();
    
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.obj2();
            }
        }).start();
    }
//两个方法之间交替执行 没有阻塞
Thread-0 : 4
Thread-1 : 4
Thread-1 : 3
Thread-0 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-0 : 0
Thread-1 : 0

共用一个对象,多线程调用obj2同步方法,因为使用的是一个对象锁,会阻塞。

String str=new String("lock"); //对象放在方法外,调用方法的时候不会新创建一个对象。
   public void obj2() {
       synchronized (str) {
           int i = 5;
           while (i-- > 0) {
               System.out.println(Thread.currentThread().getName() + " : " + i);
               try {
                   Thread.sleep(500);
               } catch (InterruptedException ie) {
               }
           }
       }
   }

      Thread-0 : 4
      Thread-0 : 3
      Thread-0 : 2
      Thread-0 : 1
      Thread-0 : 0
      Thread-1 : 4
      Thread-1 : 3
      Thread-1 : 2
      Thread-1 : 1
      Thread-1 : 0

Synchronized类锁

1.Synchronized修饰静态的方法

public static synchronized void obj3() {
           int i = 5;
           while (i-- > 0) {
               System.out.println(Thread.currentThread().getName() + " : " + i);
               try {
                   Thread.sleep(500);
               } catch (InterruptedException ie) {
               }
           }
   }

2.synchronized (test.class) ,锁的对象是test.class,即test类的锁。

public void obj1() {
        synchronized (test.class) {
           int i = 5;
           while (i-- > 0) {
               System.out.println(Thread.currentThread().getName() + " : " + i);
               try {
                   Thread.sleep(500);
               } catch (InterruptedException ie) {
               }
           }
       }
   }
  看完上面的例子,应该可以分清楚什么是对象锁和类锁了。

那么问题来了:在一个类中有两方法,分别用synchronized 修饰的静态方法(类锁)和非静态方法(对象锁)。多线程访问两个方法的时候,线程会不会阻塞?

public static synchronized void obj3() {
           int i = 5;
           while (i-- > 0) {
               System.out.println(Thread.currentThread().getName() + " : " + i);
               try {
                   Thread.sleep(500);
               } catch (InterruptedException ie) {
               }
           }
   }

 public synchronized void obj4() {
       int i = 5;
       while (i-- > 0) {
           System.out.println(Thread.currentThread().getName() + " : " + i);
           try {
               Thread.sleep(500);
           } catch (InterruptedException ie) {
           }
       }
   }

答案是???

Thread-0 : 4
Thread-1 : 4
Thread-0 : 3
Thread-1 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-1 : 0
Thread-0 : 0

不会阻塞。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值