java架构之锁常用的使用场景

  1. 锁分类

悲观锁

Synchronized、lock 适用于写的场景

乐观锁

atomicInteger、cas算法 适用于读的场景

行/表/页锁

表锁开销小加锁快不会死锁并发最低、行锁开销大加锁慢会死锁并发最高、页锁加锁时间介于前两个之间会死锁并发一般

偏向锁

锁一直被一个线程占用,没有其他线程竞争情况下,该线程可自由获取该锁,降低获取锁的代价

轻量级锁

其他线程获取该锁,发送了竞争,锁就会由偏向锁升级为轻量级锁,其他线程会通过自旋尝试获取锁,不会阻塞从而提高效率,这种锁不能替代重量级锁,只是一种过度

重量级锁

其他线程不会一直自旋,自旋到一定次数还没获取到锁,就会阻塞,锁升级为重量级锁,会让其他申请的线程进入阻塞,性能降低,jdk1.5的sync就是重量级锁

公平锁

 AbstractQueuedSynchronizer、ReentrantLock(boolean fair)参数true 要排队,挂起的线程重新开始与它真正开始运行有延迟

非公平锁

Synchronized、lock都是,因为非公平锁效率更高,不用排队,利用重新开始与真正开始的这段时间获取到锁完成操作

可重入锁

以线程为单位,线程获取对象锁后,这个线程可以再次获取本对象上的锁,而其他线程不可以,用于防范死锁

自旋锁

当一个线程在获取锁的时候,如果锁被其他线程获取,该线程将循环等待,然后不断的判断锁是否能够成功获取,直到获取才会退出循环

死锁

        等待需要的资源一直拿不到,导致线程一直处于等待中,一组线程互相等待释放锁

分布式锁

要保证一个方法在同一时间只能被一个线程执行,单机很容易解决,分布式下只能使用数据库锁、redis、zk

数据库锁:插入的时候获取锁,删除的时候释放锁

Redis:用到setnx命令,expire,dtt命令

Zk:利用zk的不能有重命子结点去获取锁,释放锁

共享锁

多个线程同时占用 reentrantReadWriteLock读锁是共享

独享锁

只有一个线程占用 reentrantReadWriteLock写锁是独享

  1. 使用
    1. 数据库锁
      1. 表锁:加的是读锁就只能对表进行读,如果是写锁也只能对表进行写
        1. lock tables 表 read local,表 read local;
        2. select sum(total) from orders;
        3. select sum(subtotal) from order_detail;
        4. unlock tables;
      2. 行锁:在select后加for update,查询到的数据会被加上一条排他锁,其他事务只能读不能写,适用于用户消费,service层查询用户余额,若余额足够则扣款,这种查询加锁,否则b用户在a用户查询到余额消费前把钱转走,会出现余额不足但扣款成功
      3. 间隙锁:update user set count=8 where id>2 and id<6; update user set count=10 where id=5;用户a还没提交事务,那么b就会阻塞
      4. 尽可能所有数据检索都通过索引完成,避免行锁升级表锁,缩小锁范围,减少索引条件,避免间隙锁,控制事务大小减少锁定时间
    2. 并发锁
      1. Sync
        1. 初级技巧
          1. 乐观锁:判断为null后才加锁,如果锁被线程获取,其他线程就不会被阻塞,直接跳过,适用于加锁部分只会被一个线程执行一次
            1. Public Object get(Object key){
            2. Object val=null;
            3.   If((val = map.get(key)) == null){
            4. Synchronized(map){
            5.   If(val = map.get(key) == null) {
            6. }}}
        2. 中级技巧
          1. Lock
            1. 相当于数据库的表锁,lock unlock
          2. Synchronized (String.intern())当string相同时,总是返回同一对象,实现对同一个用户加锁,这个用户如果查过数据库并缓存里有数据,就不需要再去查数据库了。数据很少会更新的可以用这个
            • i. public static String getSync(String uid){     String u=map.get(uid);     if(u == null){         synchronized (uid.intern()){             map.put(uid,"123456");             System.out.println(uid+"查询数据库");         }     }     return u; }
        3. 高级技巧
          1. ConcurrentHashMap
            1. 前面用到的map都是hashmap线程不安全的,而这个map是线程安全且高效的,可以直接操作数据。
    3. 分布式锁
      1. 数据库锁
        1. 对表结果中的字段设置唯一性,添加的时候如果有多条添加,那也只会有一条数据
        2. Redis,缺点用户a获取到master的锁,锁同步到slave之前master挂掉了,其他slave升级为master,用户b获取到同样的锁,安全就失效了
try{
lock = redisTemplate.opsForValue().setIfAbsent(lockKey, LOCK);
logger.info("cancelCouponCode是否获取到锁:"+lock);
if (lock) {
 redisTemplate.expire(lockKey,1, TimeUnit.MINUTES); 
//成功设置过期时间
            return res;
}else {
logger.info("cancelCouponCode没有获取到锁,不执行任务!");
}
 }finally{
     if(lock){      
            redisTemplate.delete(lockKey);
            logger.info("cancelCouponCode任务结束,释放锁!");            
        }else{
        logger.info("cancelCouponCode没有获取到锁,无需释放锁!");
     }
 }
        1. Zookeeper
          1. 分布式锁==临时节点
          2. 利用创建临时节点、节点排队监听实现阻塞(无限循环,等待最小节点释放)、获取锁突然挂掉临时节点自动删除相当于释放锁
  1. 适用角度
    1. 性能 redis>zookeeper>数据库
    2. 可靠性 zookeeper>redis>数据库
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值