synchronize关键字互斥

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

当程序运行到非静态的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同步块之间并没有互斥

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值