Java那些“锁”事 - “八锁”案例

阿里巴巴规范手册中有提到:高并发时,同步调用应该去考量锁的性能消耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。

“八锁”案例

案例一

        有一个Phone资源类,该资源类包含一个被synchronized修饰的发邮件方法和另一个synchronized修饰的发短信方法:

/**
 * 资源类
 */
class Phone {

    /**
     * 发邮件
     */
    public synchronized void sendEmail() {
        System.out.println("sendEmail success");
    }
    /**
     * 发短信
     */
    public synchronized void sendSMS() {
        System.out.println("sendSMS success");
    }
}

        只有有一部手机,A线程发邮件,B线程发短信。先发送邮件还是先发送短信?

    public static void main(String[] args) {

        Phone phone1 = new Phone();

        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();

        //保证A线程早于B线程启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone1.sendSMS();
        }, "B").start();
    }

       打印结果:

sendEmail success
sendSMS success

        结论很简单,A线程和B线程争抢的是同一个锁对象,这里的锁对象是phone1对象锁(Object Lock)。所以会先发送邮件,然后在发送短信。

案例二

         有一个Phone资源类,该资源类包含一个被synchronized修饰的发邮件方法,发送邮件的方法延时3秒和另一个synchronized修饰的发短信方法:

/**
 * 资源类
 */
class Phone {

    /**
     * 发邮件
     */
    public synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail success");
    }
    /**
     * 发短信
     */
    public synchronized void sendSMS() {
        System.out.println("sendSMS success");
    }
}

          只有有一部手机,A线程发邮件,B线程发短信。先发送邮件还是先发送短信?

    public static void main(String[] args) {

        Phone phone1 = new Phone();

        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();

        //保证A线程早于B线程启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone1.sendSMS();
        }, "B").start();

    }

        打印结果:

sendEmail success
sendSMS success

         结论很简单,A线程和B线程争抢的是同一个锁对象,这里的锁对象是phone1对象锁(Object Lock)。即使发邮件方法延时3秒钟,但是A线程依然持有phone1对象锁。所以会先发送邮件,然后在发送短信。

案例三

         有一个Phone资源类,该资源类包含一个被synchronized修饰的发邮件方法,发送邮件的方法延时3秒和另一个普通的发送短信的方法:

/**
 * 资源类
 */
class Phone {

    /**
     * 发邮件
     */
    public synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail success");
    }
    /**
     * 发短信
     */
    public  void sendSMS() {
        System.out.println("sendSMS success");
    }
}

        只有一部手机,A线程调用发邮件方法,B线程调用发短信方法。先发送邮件还是先发送短信?

    public static void main(String[] args) {

        Phone phone1 = new Phone();
        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();

        //保证A线程早于B线程启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone1.sendSMS();
        }, "B").start();

    }

        打印结果:

sendSMS success
sendEmail success

        可以看到,先发送了短信,再发送了邮件。因为发送短信方法并没有加锁,所以B线程在调用发送短信方法时并不存在锁的抢占。

案例四

         有一个Phone资源类,该资源类包含一个被synchronized修饰的发邮件方法和另一个synchronized修饰的发短信方法:

/**
 * 资源类
 */
class Phone {

    /**
     * 发邮件
     */
    public synchronized void sendEmail() {
        System.out.println("sendEmail success");
    }
    /**
     * 发短信
     */
    public synchronized void sendSMS() {
        System.out.println("sendSMS success");
    }
}

        有两部手机,A线程调用第一部手机的发邮件方法,B线程调用第二部手机的发短信方法。先发送邮件还是先发送短信?

    public static void main(String[] args) {

        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();

        //保证A线程早于B线程启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();
    }

        打印结果:

sendSMS success
sendEmail success

        这里的结论也很简单,因为这里A线程和B线程所持有的锁分别是phone1和phone2的对象锁,两个线程之间并不存在锁的抢占。所以发短信会在发邮件之前。

案例五

        有一个Phone资源类,该资源类包含一个被synchronized修饰的静态的发邮件方法和另一个synchronized修饰的静态的发短信方法:

/**
 * 资源类
 */
class Phone {

    /**
     * 发邮件
     */
    public  static  synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail success");
    }

    /**
     * 发短信
     */
    public static synchronized void sendSMS() {
        System.out.println("sendSMS success");
    }
}

        只有一部手机,A线程调用发邮件方法,B线程调用发短信方法。先发送邮件还是先发送短信?

    public static void main(String[] args) {

        Phone phone1 = new Phone();
        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();

        //保证A线程早于B线程启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone1.sendSMS();
        }, "B").start();

    }

        打印结果:

sendEmail success
sendSMS success

        这里的结果和案例一的结果一样,先发送了邮件再发送了短信。因为,此时A线程和B线程所抢占的还是同一把锁,只不过这个时候锁对象变成了Phone的类锁(Class Lock)。所以,先发送了邮件,再发送了短信。

案例六

        有一个Phone资源类,该资源类包含一个被synchronized修饰的静态的发邮件方法和另一个synchronized修饰的静态的发短信方法:

/**
 * 资源类
 */
class Phone {

    /**
     * 发邮件
     */
    public  static  synchronized void sendEmail() {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("sendEmail success");
    }

    /**
     * 发短信
     */
    public static synchronized void sendSMS() {
        System.out.println("sendSMS success");
    }
}

        两部手机,A线程调用第一部手机的发邮件方法,B线程调用第二部手机的发短信方法。先发送邮件还是先发送短信?

    public static void main(String[] args) {

        Phone phone1 = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();

        //保证A线程早于B线程启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();

    }

        结果打印:

sendEmail success
sendSMS success

        这里的结果和案例六的结果一样,虽然是两部手机,但是A线程和B线程抢占的还是同一个锁对象,就是Phone的类锁(Class Lock)。所以,先发送了邮件,再发送了短信。

案例七

        有一个Phone资源类,该资源类包含一个被synchronized修饰的静态的发邮件方法和另一个普通发短信方法:

/**
 * 资源类
 */
class Phone {

    /**
     * 发邮件
     */
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendEmail success");
    }

    /**
     * 发短信
     */
    public  void sendSMS() {
        System.out.println("sendSMS success");
    }
}

        一部手机,A线程调用发邮件方法,B线程调用发短信方法。先发送邮件还是先发送短信?

    public static void main(String[] args) {

        Phone phone1 = new Phone();

        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();

        //保证A线程早于B线程启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone1.sendSMS();
        }, "B").start();

    }

        结果打印:

sendSMS success
sendEmail success

        经过前面几个案例之后,这里的结果就显得比价简单了。应为A线程和B线程不存在锁的抢占。所以就B线程先发送了短信,A线程再发送邮件。

案例八

        有一个Phone资源类,该资源类包含一个被synchronized修饰的静态的发邮件方法和另一个普通发短信方法:

/**
 * 资源类
 */
class Phone {

    /**
     * 发邮件
     */
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendEmail success");
    }

    /**
     * 发短信
     */
    public  void sendSMS() {
        System.out.println("sendSMS success");
    }
}

        两部手机,A线程调用第一步手机发送邮件,B线程调用第二部手机的发送短信方法。先发送邮件还是先发送短信?

 public static void main(String[] args) {

        Phone phone1 = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();

        //保证A线程早于B线程启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone2.sendSMS();
        }, "B").start();
    }

        打印结果:

sendSMS success
sendEmail success

        该案例中的A线程持有的是Phone类锁(Class Lock),而B线程并不存在和A线程抢占锁的关系,所以先发送了短息,再发送了邮件。

通过上面几个案例,同学们对类锁(Class Lock)和对像锁(Object Lock)有一定的认识了吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值