(锁) 系列篇 --“一、锁的基本概念”

 

(锁) 系列篇

 

锁的概念,更多源于生活。每家每户都有一把锁,只有持有钥匙打开锁才能进入房屋,以此来防止盗贼进入家中。

 

 

1、为什么需要锁?

 

 

引申到软件世界高并发场景,锁即为竞争资源。只有拿到锁才能访问资源,否则进入锁等待区,以此保护资源顺序性访问防止乱序访问。

 

2、Java对象锁

 

 

Java每个对象都是Object的子类有且只有一个锁,言外之意每个对象都可作为互斥资源来实现有序访问。以32位JVM对象头为例,根据锁状态(后续补充)具有不同标志位

 

 (图片来自《Java并发编程的艺术》)

 

对象锁有两个等待池:

  • Entry Set池:等待获锁池,当锁被释放之后有机会竞争获锁;
  • Wait Set池:等待挂起池,调用wait之后进入该池,不能直接竞争获锁。只有当锁对象调用notify、notifyAll 唤醒后进入Entry Set池,才可竞争获锁;

 

 

 

/**
 * Class {@code Object} is the root of the class hierarchy.
 * Every class has {@code Object} as a superclass. All objects,
 * including arrays, implement the methods of this class.
 *
 * @author  unascribed
 * @see     java.lang.Class
 * @since   JDK1.0
 */
public class Object {
    //........
     
    // 锁基本操作方法:获取锁、释放锁
    public final native void notify();
    public final native void notifyAll();
    public final native void wait(long timeout) throws InterruptedException;
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
    public final void wait() throws InterruptedException {
        wait(0);
    }
}

经常会被问到wait、notify方法为什么不放在Thread类里面,而是在Object类中?

实质是wait、notify都属于锁本身操作而非线程操作。每个对象都有且只有一个锁,定义在Object类中每个子类都将继承这些基本的操作,实现代码复用。

 

3、分布式锁

 

 

前面引申的锁概念都发生进程内,而在分布式环境的资源竞争就起不到作用。例如秒杀服务大多都是分布式部署,库存为100而参与秒杀用户为1w,如何保证不超卖和少卖呢?

此时需要用到分布式锁将资源状态外置,保证资源一致性。比如常用Mysql行锁或者表锁进行控制,将锁的实现细节丢给数据库,保证库存更新的顺序性。

然而锁往往会成为数据库负担,可引入redis、zookeeper分布式锁缓解数据库压力。需要注意的是大并发场景其实不适合使用锁,这里只是做个引申示例。

 

 

 

 

posted on 2020-03-23 13:54  小猩  阅读(622)  评论(0编辑  收藏  举报

导航