深入理解Synchronized(对象锁)和Static Synchronized(类锁)的区别

在方法上Synchronized和Static Synchronized区别

synchronized是对类的当前实例(当前对象)进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”, 类的两个不同实例就没有这种约束了。

那么static synchronized恰好就是要控制类的所有实例的并发访问,static synchronized是限制多线程中该类的所有实例同时访问jvm中该类所对应的代码块。实际上,在类中如果某方法或某代码块中有 synchronized,那么在生成一个该类实例后,该实例也就有一个监视块,防止线程并发访问该实例的synchronized保护块,而static synchronized则是所有该类的所有实例公用得一个监视块,这就是他们两个的区别。也就是说synchronized相当于 this.synchronized,而static synchronized相当于Something.synchronized.(后面又讲解)

一个日本作者-结成浩的《java多线程设计模式》有这样的一个列子:

pulbic class Something(){
    public synchronized void isSyncA(){}
    public synchronized void isSyncB(){}
    public static synchronized void cSyncA(){}
    public static synchronized void cSyncB(){}
}

那么,假如有Something类的两个实例x与y,那么下列各组方法被多线程同时访问的情况是怎样的?

a. x.isSyncA()与x.isSyncB() 
b. x.isSyncA()与y.isSyncA()
c. x.cSyncA()与y.cSyncB()
d. x.isSyncA()与Something.cSyncA()

这里,很清楚的可以判断:
a,都是对同一个实例(x)的synchronized域访问,因此不能被同时访问。(多线程中访问x的不同synchronized域不能同时访问)
如果在多个线程中访问x.isSyncA(),因为仍然是对同一个实例,且对同一个方法加锁,所以多个线程中也不能同时访问。(多线程中访问x的同一个synchronized域不能同时访问)

b,是针对不同实例的,因此可以同时被访问(对象锁对于不同的对象实例没有锁的约束)

c,因为是static synchronized,所以不同实例之间仍然会被限制,相当于Something.cSyncA()与 Something.cSyncB()了,因此不能被同时访问。

第d呢?,书上的 答案是可以被同时访问的,答案理由是synchronzied的是实例方法与synchronzied的类方法由于锁定(lock)不同的原因。
个人分析也就是synchronized 与static synchronized 相当于两帮派,各自管各自,相互之间就无约束了,可以被同时访问。
举个例子:

public class Demo19 {
    /*
    *   2.2.8     静态同步synchronized方法与synchronized(class)代码块
    * */

    public static void main(String[] args) {
        Demo19Service service = new Demo19Service();
        Thread t1 = new Demo19ThreadA(service);
        t1.setName("A");
        t1.start();
        Thread t2 = new Demo19ThreadB(service);
        t2.setName("B");
        t2.start();

        /*
        * 运行结果:
        *
            A进入foo1方法在1647665063051
            B进入foo2方法在1647665063051
            A结束foo1方法在1647665065052
            B结束foo2方法在1647665065052
        *
        * */

        /*
        * 结论:
        *
        *   以异步方式运行的原因是:持有的锁不一样,非静态方法持有的是对象锁,而静态方法持有的是Class锁,
        *   Class锁可以对类的所有实例对象起作用
        *
        *   很多人可能会有疑惑,既然Class锁可以对类的所有实例对象起作用,那为什么还可以异步执行呢?
        *
        *   这里解释一下
        *   类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。
        *
        *   类锁:可以理解为当前类所有实例出来的对象的锁,通俗点点讲就是 把所有new出来的对象的锁
        *   对象锁:可以理解为 当前类 new 出来的对象的锁,新new 对象是一把新锁,但是这把锁的性质是和前面new 出来的对象的锁是一样的
        * */
    }
}

class Demo19Service{
    synchronized public static void foo1(){
        System.out.println(Thread.currentThread().getName() + "进入foo1方法在" + System.currentTimeMillis());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "结束foo1方法在" + System.currentTimeMillis());
    }

    synchronized public void foo2(){
        System.out.println(Thread.currentThread().getName() + "进入foo2方法在" + System.currentTimeMillis());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "结束foo2方法在" + System.currentTimeMillis());
    }
}

class Demo19ThreadA extends Thread{
    private Demo19Service service;
    public Demo19ThreadA(Demo19Service service){
        this.service = service;
    }

    @Override
    public void run() {
        service.foo1();
    }
}

class Demo19ThreadB extends Thread{
    private Demo19Service service;
    public Demo19ThreadB(Demo19Service service){
        this.service = service;
    }

    @Override
    public void run() {
        service.foo2();
    }
}

上面代码synchronized同时修饰静态方法和实例方法,但是运行结果是交替进行的,这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

结论

  • synchronized static
    • 是某个类的范围,synchronized static cSync{}防止***多个线程中多个实例***同时访问这个 类中的synchronized static 方法。它可以对类的所有对象实例起作用。
    • 通俗点讲就是,Something.cSync()方法和所有Somethingnew的对象.cSync()方法是持有同一个锁,是同步执行的。
  • synchronized
    • 是某实例的范围,synchronized isSync(){}防止多个线程中这一个实例同时访问这个类的synchronized 方法。

有参考这篇博客相关内容(写的还是不够通俗易懂,所以本人自己又重新写了下自己的理解):https://blog.csdn.net/cs408/article/details/48930803

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值