synchronize关键字互斥

本文深入探讨Java中的同步机制,包括synchronized关键字的使用方法及其如何帮助实现线程间的互斥和同步。通过具体示例说明不同锁类型的特性和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放

这样讲有点抽象看下面的这个例子

首先定义两个类,一个是House,House中bathroom和kitchen都是竞争的资源

class House {
    private String bathRoom = "bathroom";
    private String kitChen = "kitchen";

    //静态同步方法修房子
    public synchronized  static void repair(String username){
        System.out.println(username + " is repairing " );
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(username + " finish repairing ");
    }
    //使用同步方法使用厨房
    public synchronized void useKitChen1(String username) {
        System.out.println(username + " is using " + kitChen);
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(username + " finish using " + kitChen);
    }
    //使用同步块使用厨房
    public void useKitchen2(String username) {
        synchronized (kitChen) {
            System.out.println(username + " is using " + kitChen);
            try {
                TimeUnit.SECONDS.sleep(5);
                int i = 45 + 5;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(username + " finish using " + kitChen);
        }
    }

    //使用同步方法使用浴室
    public synchronized void useBathRoom1(String username) {
        System.out.println(username + " is using " + bathRoom);
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(username + " finish using " + bathRoom);
    }

    //使用同步快使用浴室
    public void useBathRoom2(String username) {
        synchronized (bathRoom) {
            System.out.println(username + " is using " + bathRoom);
            try {
                TimeUnit.SECONDS.sleep(5);
                int k = 6 + 5 + 8 + 9;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(username + " finish using " + bathRoom);
        }
    }

}

第二个类是Parent,也就是房子的使用者

class Parent implements Runnable {
    House house;
    String name;

    public Parent(House house, String name) {
        this.house = house;
        this.name = name;
    }

    @Override
    public void run() {
        if (name.equals("mother")){
            house.useBathRoom1(name);
        }else{
            house.useBathRoom2(name);
        }
    }
}

首先验证同步方法之间的互斥

 public static void main(String[] args) {
        House house=new House();
        Parent mom=new Parent(house,"mother");
        Parent father=new Parent(house,"father");
        new Thread(mom).start();
        new Thread(father).start();
    }

运行结果

mother is using bathroom
mother finish using bathroom
father is using kitchen
father finish using kitchen

可以看到同步方法之间是互斥的,虽然mother用的是bathroom ,father使用的是kitchen,按理说浴室和厨房的共同使用是不冲突的,但是由于mother和father使用的都是同步方法,他们使用bathroom和kitchen方法是锁房门(也就是锁住整个house对象),只要有一个人使用了house中的资源,其它任何人都不能使用house的任何资源

浴室和厨房的共同使用时不冲突的,上述这样的使用明显是不合理的

我们进行如下的优化

我们将Parent中的run()方法进行如下的修改,修改成同步快使用资源

public void run() {
        if (name.equals("mother")) {
            house.useBathRoom2(name);
        } else {
            house.useKitchen2(name);
        }
    }

运行结果

mother is using bathroom
father is using kitchen
father finish using kitchen
mother finish using bathroom

可以看到father 使用kitchen和mother使用bathroom之间没有任何干扰了,他们可以同时使用

实际上同步方法可以这样改成同步快

public void useKitchen(String username) {
        synchronized (this) {
            System.out.println(username + " is using " + kitChen);
            try {
                TimeUnit.SECONDS.sleep(5);
                int i = 45 + 5;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(username + " finish using " + kitChen);
        }
    }

this就相当于当前这个对象

静态同步方法和静态同步块使用上类似

只不过静态同步方法拿到的锁不是对象锁而是整个类的锁House.class,拿到这个锁后,某个线程使用静态同步方法其它任何线程都不能使用该类的静态同步方法

House.class这个锁和对象锁之间没有关系,看下面这个例子

还是修改Parent的run方法

if (name.equals("mother")) {
            house.useBathRoom1(name);
        } else {
            House.repair(name);
        }

运行结果

mother is using bathroom
father is repairing 
father finish repairing 
mother finish using bathroom

可以看到mother使用bathroom的同时不影响father repair。mother虽然拿到了house的对象锁,但是没有拿到House.class这个锁,所以这两者互不影响

注意:我们来看下面一个mother和father同时使用kitchen的例子,这两个并不会互斥

修改run方法为如下

@Override
    public void run() {
        if (name.equals("mother")) {
            house.useKitChen1(name);
        } else {
            house.useKitchen2(name);
        }
    }

运行结果

mother is using kitchen
father is using kitchen
mother finish using kitchen
father finish using kitchen

同步方法和非this同步块之间并没有互斥

synchronize 关键字是用于实现线程同步的一种机制,其原理是通过在代码块或方法上加锁,来确保多个线程在执行该代码段时的互斥性。其实现原理如下: 1. 锁对象:synchronize 使用的锁对象可以是任意对象。当一个线程进入 synchronized 代码块时,它会尝试获取锁;如果锁没有被其他线程占用,则该线程获取锁,否则该线程将进入阻塞状态,直到锁被释放。 2. 对象监视器:在 Java 对象的内部数据结构中,每个对象都有一个关联的对象监视器。对象监视器负责管理锁的获取和释放,以及线程在对象上的等待和唤醒操作。当一个线程成功获取锁时,对象监视器会记录该线程的标识,并将对象的锁计数器加一。同时,其他线程在尝试获取锁时,将进入对象的等待队列,等待锁的释放。 3. 原子性操作:synchronize 关键字使得一些复合操作具有原子性。即当一个线程执行 synchronized 代码块时,其他线程无法同时进入该代码块,保证了多个线程对共享数据的同步访问。这样可以避免出现并发访问共享资源导致的数据不一致性和线程安全问题。 总之,synchronize 实现线程同步的原理是通过加锁和对象监视器来实现的。当多个线程竞争同一个锁时,只有一个线程能够获得锁,并执行 synchronized 代码块,其他线程将进入阻塞状态,直到锁被释放。这样就确保了共享数据的安全访问和操作的原子性,避免了多线程并发执行导致的数据不一致性问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值