【Spring Boot 17,最新精心整理Java面试题

1、使用Redis实现分布式锁原因


(1)Redis有很高的性能;

(2)Redis命令对此支持较好,实现起来比较方便

2、使用命令简介


(1) setnx

SETNX key val:当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。

(2)expire

expire key timeout:为key设置一个超时时间,单位是秒,超过这个时间锁会自动释放,避免死锁。

(3)delete

删除key。

3、实现思想


(1)获取锁的时候,使用 setnx 加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

(2)获取锁的时候还设置了一个获取的超时时间,若超过这个时间则放弃获取锁。

(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

4、分布式锁的简单代码



/**

 * 分布式锁的简单实现代码

 * Created by 素小暖 on 2020/2/12.

 */

public class DistributedLock {

 

    private final JedisPool jedisPool;

 

    public DistributedLock(JedisPool jedisPool) {

        this.jedisPool = jedisPool;

    }

 

    /**

     * 加锁

     * @param lockName       锁的key

     * @param acquireTimeout 获取超时时间

     * @param timeout        锁的超时时间

     * @return 锁标识

     */

    public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {

        Jedis conn = null;

        String retIdentifier = null;

        try {

            // 获取连接

            conn = jedisPool.getResource();

            // 随机生成一个value

            String identifier = UUID.randomUUID().toString();

            // 锁名,即key值

            String lockKey = "lock:" + lockName;

            // 超时时间,上锁后超过此时间则自动释放锁

            int lockExpire = (int) (timeout / 1000);

 

            // 获取锁的超时时间,超过这个时间则放弃获取锁

            long end = System.currentTimeMillis() + acquireTimeout;

            while (System.currentTimeMillis() < end) {

                if (conn.setnx(lockKey, identifier) == 1) {

                    conn.expire(lockKey, lockExpire);

                    // 返回value值,用于释放锁时间确认

                    retIdentifier = identifier;

                    return retIdentifier;

                }

                // 返回-1代表key没有设置超时时间,为key设置一个超时时间

                if (conn.ttl(lockKey) == -1) {

                    conn.expire(lockKey, lockExpire);

                }

 

                try {

                    Thread.sleep(10);

                } catch (InterruptedException e) {

                    Thread.currentThread().interrupt();

                }

            }

        } catch (JedisException e) {

            e.printStackTrace();

        } finally {

            if (conn != null) {

                conn.close();

            }

        }

        return retIdentifier;

    }

 

    /**

     * 释放锁

     * @param lockName   锁的key

     * @param identifier 释放锁的标识

     * @return

     */

    public boolean releaseLock(String lockName, String identifier) {

        Jedis conn = null;

        String lockKey = "lock:" + lockName;

        boolean retFlag = false;

        try {

            conn = jedisPool.getResource();

            while (true) {

                // 监视lock,准备开始事务

                conn.watch(lockKey);

                // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁

                if (identifier.equals(conn.get(lockKey))) {

                    Transaction transaction = conn.multi();

                    transaction.del(lockKey);

                    List<Object> results = transaction.exec();

                    if (results == null) {

                        continue;

                    }

                    retFlag = true;

                }

                conn.unwatch();

                break;

            }

        } catch (JedisException e) {

            e.printStackTrace();

        } finally {

            if (conn != null) {

                conn.close();

            }

        }

        return retFlag;

    }

}

5、测试


例子中使用50个线程模拟秒杀一个商品,使用–运算符来实现商品减少,从结果有序性就可以看出是否为加锁状态。

模拟秒杀服务,在其中配置了jedis线程池,在初始化的时候传给分布式锁,供其使用。


/**

 * Created by 素小暖 on 2020/2/12.

 */

public class Service {

 

    private static JedisPool pool = null;

 

    private DistributedLock lock = new DistributedLock(pool);

 

    int n = 500;

 

    static {

        JedisPoolConfig config = new JedisPoolConfig();

        // 设置最大连接数

        config.setMaxTotal(200);

        // 设置最大空闲数

        config.setMaxIdle(8);

        // 设置最大等待时间

        config.setMaxWaitMillis(1000 * 100);

        // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的

        config.setTestOnBorrow(true);

        pool = new JedisPool(config, "127.0.0.1", 6379, 3000);

    }

 

    public void seckill() {

        // 返回锁的value值,供释放锁时候进行判断

        String identifier = lock.lockWithTimeout("resource", 5000, 1000);

        System.out.println(Thread.currentThread().getName() + "获得了锁");

        System.out.println(--n);

        lock.releaseLock("resource", identifier);

    }

}

结果如下,有序的:

若注释使用锁的部分:


public void seckill() {

    // 返回锁的value值,供释放锁时候进行判断

    //String indentifier = lock.lockWithTimeout("resource", 5000, 1000);

    System.out.println(Thread.currentThread().getName() + "获得了锁");

    System.out.println(--n);

    //lock.releaseLock("resource", indentifier);

}

从结果可以看出,有一些是异步进行的:

六、基于Zookeeper实现分布式

======================

Zookeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一目录下只能有一个唯一文件名。

1、基于Zookeeper实现分布式锁的步骤如下


(1)创建一个目录mylock;

(2)创建A想获取锁就在mylock目录下创建临时顺序节点;

总结

对于面试,一定要有良好的心态,这位小伙伴面试美团的时候没有被前面阿里的面试影响到,发挥也很正常,也就能顺利拿下美团的offer。
小编还整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家,
CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

在这里插入图片描述

在这里插入图片描述

1、基于Zookeeper实现分布式锁的步骤如下


(1)创建一个目录mylock;

(2)创建A想获取锁就在mylock目录下创建临时顺序节点;

总结

对于面试,一定要有良好的心态,这位小伙伴面试美团的时候没有被前面阿里的面试影响到,发挥也很正常,也就能顺利拿下美团的offer。
小编还整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家,
CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

[外链图片转存中…(img-WRX7CwFR-1630924724095)]

[外链图片转存中…(img-NoBa1mcX-1630924724096)]

最后感谢大家的支持,希望小编整理的资料能够帮助到大家!也祝愿大家都能够升职加薪!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值