synchronized 修饰普通方法与静态方法的区别

本文通过三个示例详细解析了Java中synchronized修饰普通方法和静态方法时的不同效果。当修饰普通方法时,锁住的是对象实例,不同对象可以并发执行;而修饰静态方法时,锁住的是类,导致所有对象执行该方法时都会互斥。

面试时候被问的问题,当时脑子里闪了一下,但是没答出来

先说结论:

  • synchronized 修饰普通方法时,锁的是当前对象的方法,等价于 synchronized (this)
  • synchronized 修饰静态方法时,锁的是所有对象的方法,等价于 synchronized (Xxx.class)

我们知道,普通方法从属于对象,而静态方法从属于类
带着疑惑,来看代码吧


情景1:当 synchronized 修饰 普通方法后,同一个对象被两个线程调用 结果

public class TestMain {
    public static void main(String[] args) {
        //测试 synchronized 修饰普通方法
        Person person1 = new Person();

        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println("t1开始");
                person1.work("t1线程老板 叫person1 工作啦");
                System.out.println("t1结束");
            }
        };

        Thread t2 = new Thread() {
            @Override
            public void run() {
                System.out.println("t2开始");
                person1.work("t2线程老板 叫person1 工作啦");
                System.out.println("t2结束");
            }
        };
        t1.start();
        t2.start();

    }
}
class Person{

    //输出10次value work 方法
    public synchronized void work(String value){
        for (int i=0;i<10;i++){
            System.out.println(value + i );
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

打印结果

t1开始
t2开始
t1线程老板 叫person1 工作啦0
t1线程老板 叫person1 工作啦1
t1线程老板 叫person1 工作啦2
t1线程老板 叫person1 工作啦3
t1线程老板 叫person1 工作啦4
t1线程老板 叫person1 工作啦5
t1线程老板 叫person1 工作啦6
t1线程老板 叫person1 工作啦7
t1线程老板 叫person1 工作啦8
t1线程老板 叫person1 工作啦9
t1结束
t2线程老板 叫person1 工作啦0
t2线程老板 叫person1 工作啦1
t2线程老板 叫person1 工作啦2
t2线程老板 叫person1 工作啦3
t2线程老板 叫person1 工作啦4
t2线程老板 叫person1 工作啦5
t2线程老板 叫person1 工作啦6
t2线程老板 叫person1 工作啦7
t2线程老板 叫person1 工作啦8
t2线程老板 叫person1 工作啦9
t2结束

上面代码我们可以看到,虽然 t1 和 t2 线程都开始了,但是 t2 线程并没有打断 t1 线程执行wok1 方法 (注意此时都是person1 一个对象)


情景2:synchronized 修饰普通方法后,不同对象被不同的线程调用结果


public class TestMain {
    public static void main(String[] args) {
        //测试 synchronized 修饰普通方法
        Person person1 = new Person();
        Person person2 = new Person();  // 修改 

        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println("t1开始");
                person1.work("t1线程老板 叫person1 工作啦");
                System.out.println("t1结束");
            }
        };

        Thread t2 = new Thread() {
            @Override
            public void run() {
                System.out.println("t2开始");
                person2.work("t2线程老板 叫person2 工作啦"); // 修改
                System.out.println("t2结束");
            }
        };
        t1.start();
        t2.start();

    }
}
class Person{

    //输出10次value work1 方法
    public synchronized void work(String value){
        for (int i=0;i<10;i++){
            System.out.println(value + i );
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

t1开始
t2开始
t1线程老板 叫person1 工作啦0
t2线程老板 叫person2 工作啦0
t1线程老板 叫person1 工作啦1
t2线程老板 叫person2 工作啦1
t2线程老板 叫person2 工作啦2
t1线程老板 叫person1 工作啦2
t1线程老板 叫person1 工作啦3
t2线程老板 叫person2 工作啦3
t1线程老板 叫person1 工作啦4
t2线程老板 叫person2 工作啦4
t2线程老板 叫person2 工作啦5
t1线程老板 叫person1 工作啦5
t1线程老板 叫person1 工作啦6
t2线程老板 叫person2 工作啦6
t2线程老板 叫person2 工作啦7
t1线程老板 叫person1 工作啦7
t1线程老板 叫person1 工作啦8
t2线程老板 叫person2 工作啦8
t1线程老板 叫person1 工作啦9
t2线程老板 叫person2 工作啦9
t2结束
t1结束

我们可以看到 ,这两个对象的方法在执行的过程中 都被打断了


情景3 : 情景2 的基础上 使用 static 修饰work方法

package com.demo06;

public class TestMain {
    public static void main(String[] args) {
        //测试 synchronized 修饰普通方法
        Person person1 = new Person();
        Person person2 = new Person();  // 修改

        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println("t1开始");
                person1.work("t1线程老板 叫person1 工作啦");
                System.out.println("t1结束");
            }
        };

        Thread t2 = new Thread() {
            @Override
            public void run() {
                System.out.println("t2开始");
                person2.work("t2线程老板 叫person2 工作啦"); // 修改
                System.out.println("t2结束");
            }
        };
        t1.start();
        t2.start();

    }
}
class Person{

    //输出10次value work1 方法
    public static synchronized void work(String value){ //改动
        for (int i=0;i<10;i++){
            System.out.println(value + i );
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

t2开始
t2线程老板 叫person2 工作啦0
t1开始
t2线程老板 叫person2 工作啦1
t2线程老板 叫person2 工作啦2
t2线程老板 叫person2 工作啦3
t2线程老板 叫person2 工作啦4
t2线程老板 叫person2 工作啦5
t2线程老板 叫person2 工作啦6
t2线程老板 叫person2 工作啦7
t2线程老板 叫person2 工作啦8
t2线程老板 叫person2 工作啦9
t2结束
t1线程老板 叫person1 工作啦0
t1线程老板 叫person1 工作啦1
t1线程老板 叫person1 工作啦2
t1线程老板 叫person1 工作啦3
t1线程老板 叫person1 工作啦4
t1线程老板 叫person1 工作啦5
t1线程老板 叫person1 工作啦6
t1线程老板 叫person1 工作啦7
t1线程老板 叫person1 工作啦8
t1线程老板 叫person1 工作啦9
t1结束

我们可以看到,两个对象执行方法是又互不打断啦 。

到最后发现,我也可以说成 static 修饰 synchronized 方法后,使这个类的所有实例执行该方法时 都互不中断。synchronized 作用同步了所有的对象

`synchronized` 关键字用于控制线程对共享资源的访问,确保在任一时刻只有一个线程能执行该代码块或方法。在 Java 中,将 `synchronized` 修饰静态方法非静态(实例)方法会带来一些不同: 1. 静态同步 (synchronized static): - 当在静态方法上使用 `synchronized` 时,是对当前类(Class 对象)的引用,而不是具体的对象实例。这意味着所有对该类的调用都会共享同一把,无论它们来自哪个对象。 - 因此,如果两个不同的线程同时持有这个类的静态,那么只有其中一个能够执行静态同步的方法。 - 好处在于简化了定管理,因为不需要为每个实例创建独立的。但这也意味着线程之间的可见性状态一致性可能不像基于实例的同步那么容易理解。 2. 实例同步 (synchronized on instance): - 在实例方法上使用 `synchronized`,是该方法所在的对象实例。每个调用该方法的具体对象都会获得自己的,所以并发地调用相同方法的不同对象是可以并行执行的。 - 这有助于维护每个对象的状态独立性,但需要更精确地管理,尤其是当多个方法需要互斥访问同一个实例变量时。 - 如果一个类的所有实例都需要同步,那通常使用静态更为合适,因为静态更容易管理避免死。 总结一下,关键区别在于: - 静态同步适用于类级别的资源共享,所有对象共享一把; - 实例同步针对单个对象,每个实例有自己的,适合处理特定对象的数据并发操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值