一、概述

学习完lock锁之后,想要来写一把自己的lock锁。

但是我感觉比较初级,应该在学习Lock锁之前来写的。反正也想着总结一下。

二、lock锁的前提

1、锁的标识,当前锁是用state来进行标识的;

2、加锁、解锁方法;

3、如果锁是自由状态,加锁成功的话需要让其返回加锁成功状态;

4、如果锁不是自由状态,则让其加锁失败。所谓的加锁失败,是让其线程陷入到阻塞中或者是自旋;

5、解锁方法原则:释放锁的同时还需要唤醒其他线程(当前锁中使用的是自旋,所以不需要唤醒)

对于第三点要来做个详细说明:

存在一个现象,对于外部线程来说,不知道锁是否是自由状态。 那么就意味着,外部线程来访问锁的时候,就分为了两种情况:被其他线程持有锁 或者 是当前锁是自由状态, 那么对于多线程来说:t1,t2,t3,t4,如果当前锁被t1持有,t2,t3,t4线程因为持有不到锁,而去自旋,但是此时,如果t5线程进入,而t1线程又刚刚好将锁释放掉,而锁又被刚刚加入被t5持有了,那么对于t2,t3,t4线程来说,是不公平的,因为已经自旋了这么久了。因为外部的线程到来而导致当前线程无法使用。所以需要考虑到公平和非公平的条件。

如果是公平锁,那么线程也应该等着前面的t2,t3,t4线程执行完之后,然后轮到t5;如果是不公平的,那么就无所谓
但是如果是多线程条件下,自旋是很消耗CPU资源的。所以为了避免CPU资源的消耗,应该考虑让线程阻塞。那么阻塞应该要被唤醒, 那么唤醒,那么就应该考虑到应该使用容器来盛放多线程,在锁释放之后,将线程再唤醒

三、代码实现

public class LigLock {

    private final static Logger logger = LoggerFactory.getLogger(LigLock.class);

    //标识---加锁成功=1  如果自由状态=0
    volatile int state=0;

    //unsafe 对象,因为cas操作需要这个对象
    static Unsafe unsafe;

    //state这个字段在ShadowLock对象当中的内存偏移量
    private static long stateOffset;

    /**
     * 1、获取unsafe对象,这个对象的获取稍微有点复杂,目前不需要关心
     * 2、通过unsafe对象得到state属性在当前对象当的偏移地址
     */
    static {
        try {
            unsafe = getUnSafe();
            stateOffset = unsafe.objectFieldOffset(LigLock.class.getDeclaredField("state"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //加锁方法
    @SneakyThrows
    public void lock(){
        //compareAndSet(0,1)  当某个属性(state)他的值如果为0 则修改1

        //效率高 cas 无锁 不会发生内核态切换

        //t1 执行 compareAndSet(0,1) before state =0;after  true ---->state=1
        //t1 执行 compareAndSet(0,1) before state =1

        // 这里保证了原子性操作
        while(!compareAndSet(0,1)){
            //加锁失败
            logger.info("当前线程{}在等待获取得到锁",Thread.currentThread().getName());
            Thread.sleep(10L);
        }
        // 如果成功,则进行赋值
        Thread currentThread = Thread.currentThread();
        logger.info("当前线程获取得到了锁,当前线程的名称是:{}",currentThread.getName());
    }

    public void unlock(){
        //思考这里为什么不要cas? 因为这里不需要来进行只有已经持有锁了的线程才可以操作
        state=0;
        logger.info("当前线程释放了锁,当前线程的名称是:{}",Thread.currentThread().getName());
    }


    //获取unsafe对象
    private static Unsafe getUnSafe() throws NoSuchFieldException, IllegalAccessException {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        return (Unsafe) theUnsafe.get(null);
    }

    //cas 修改state的值
    //expect预期值,如果相同则把state的值改成x 原子操作
    private boolean compareAndSet(int expect,int x){
        //stateOffset 一个地址 【state属性的地址】
        return unsafe.compareAndSwapInt(this, stateOffset, expect, x);
    };


}

测试代码:

public class LigLockTest {

    static LigLock lock = new LigLock();
    private final static Logger logger = LoggerFactory.getLogger(LigLockTest.class);
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            lock.lock();
            logger.info("当前是线程{}在执行",Thread.currentThread().getName());
            try {
                TimeUnit.MICROSECONDS.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.unlock();
            logger.info("-------------------------");
        }, "t1");

        Thread t2 = new Thread(() -> {
            lock.lock();
            logger.info("当前是线程{}在执行",Thread.currentThread().getName());
            lock.unlock();
            logger.info("-------------------------");
        }, "t2");

        t1.start();
        TimeUnit.MICROSECONDS.sleep(10);
        t2.start();
    }

}

四、缺点总结

1、不能够支持重入;

2、不支持公平锁;

3、性能较低。因为自旋比较消耗CPU资源;

4、没有对排队线程记录以及当前线程记录;

原文地址:http://www.cnblogs.com/likeguang/p/16810649.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性