悲观锁和排他锁有哪些基础概念?该怎么实现?

TheDisguiser 2020-05-30 14:20:39 java常见问答 10515

对于学习过数据库的来说,各种锁机制应该都不陌生吧,那你们知道悲观锁和排它锁基础概念是什么吗?它们又都该怎么实现呢?快一起来瞧瞧吧。

一、悲观锁概念及实现详解

悲观锁是一种数据库锁机制,它会总是假设最坏的情况,在每次线程去拿数据时都认为其他线程会修改数据,所以,每次在拿数据的时候它都会上锁,这样其他线程想拿这个数据就会一直阻塞,直到它拿到锁为止。

传统关系型数据库里就用到了很多这种锁机制,如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

悲观锁具体实现:

POJO类

class ProductStock
{
    private Long productId; //商品id
    private Integer number; //库存量
    public Long getProductId()
    {
        return productId;
    }
    public void setProductId(Long productId)
    {
        this.productId = productId;
    }
    public Integer getNumber()
    {
        return number;
    }
    public void setNumber(Integer number)
    {
        this.number = number;
    }
}

实现类

/**
 * 更新库存(使用悲观锁)
 * @param productId
 * @return
 */
public boolean updateStock(Long productId)
{
    //先锁定商品库存记录
    ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId} FOR UPDATE", productId);
    if (product.getNumber() > 0)
    {
        int updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId}", productId);
        if (updateCnt > 0)
        { //更新库存成功
            return true;
        }
    }
    return false;
}

二、排它锁概念及实现详解

排他锁(X锁),又被叫做写锁、独占锁,这是一种基本的锁类型。若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A。

排它锁具体实现:

lock锁实现

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class Mutex implements Lock
{
    //创造一个同步器
    //该同步器用一个state来维护,当state表示同一时间当前同步状态的线程数量
    private static class Sync extends AbstractQueuedSynchronizer
    {
        @Override
        protected boolean tryAcquire(int arg)
        {
            if (compareAndSetState(0, arg))
            { //CAS操作的原子性,若当前线程设置成1,其他线程无法设置成功
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        @Override
        protected boolean tryRelease(int arg)
        {
            if (getState() == 0) throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0); //由于获取到了锁,因此这一步无需通过CAS操作保证原子性
            return true;
        }
        //是否处于占用状态
        @Override
        protected boolean isHeldExclusively()
        {
            return getState() == 1;
        }
        //返回一个Condition
        Condition newCondition()
        {
            return new ConditionObject();
        }
    }
    private final Sync sync = new Sync(); //final域能保证在初始化之前只对一个线程可见
    @Override
    public void lock()
    {
        sync.acquire(1);
    }
    @Override
    public void lockInterruptibly() throws InterruptedException
    {
        sync.acquireInterruptibly(1);
    }
    @Override
    public boolean tryLock()
    {
        return sync.tryAcquire(1);
    }
    @Override
    public void unlock()
    {
        sync.release(1);
    }
    @Override
    public Condition newCondition()
    {
        return sync.newCondition();
    }
    public boolean isLocked()
    {
        return sync.isHeldExclusively();
    }
    //判断当前同步队列中是否还有等待获取锁
    public boolean hasQueuedThreads()
    {
        return sync.hasQueuedThreads();
    }
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException
    {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
}

自定义锁使用

import java.util.concurrent.locks.Lock;
public class Test
{
    public static void main(String[] args)
    {
        final Lock lock = new Mutex();
        Thread t1 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                lock.lock();
                try
                {
                    for (int i = 0; i < 10; i++)
                    {
                        try
                        {
                            System.out.println(Thread.currentThread()
                                .getName() + "-" + i);
                            Thread.sleep(100);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
                finally
                {
                    lock.unlock();
                }
            }
        }, "Thread1");
        Thread t2 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                lock.lock();
                try
                {
                    for (int i = 0; i < 10; i++)
                    {
                        try
                        {
                            System.out.println(Thread.currentThread()
                                .getName() + "-" + i);
                            Thread.sleep(100);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
                finally
                {
                    lock.unlock();
                }
            }
        }, "Thread2");
        t1.start();
        t2.start();
    }
}

以上就是关于悲观锁及排它锁的所有内容了,如果还想了解更多锁机制相关的java常见问答知识,就请一直关注我们网站吧。

推荐阅读:

悲观锁提供缓存功能需要怎么处理?悲观锁是什么?

乐观锁和悲观锁的区别有什么?有什么作用?

乐观锁和悲观锁详解,具体概念介绍