锁的分类

悲观锁与乐观锁

悲观锁比较悲观,当多个线程对同一行数据实现修改的时候,最后只有一个线程才能修改成功,只要谁能够对获取该到行锁则其他线程时不能够对该数据做任何修改操作,且是阻塞状态。 比如for update 或者事务开启了事务但是没有提交事务。

乐观锁比较乐观,通过预值或者版本号比较,如果不一致性的情况则通过循环控制修改,当前线程不会被阻塞,是乐观,效率比较高。

UPDATE meite_user SET user_name=?, version=? WHERE user_id=? AND version=? AND deleted=0 

SELECT user_id,user_name,user_age,user_addres,create_time,deleted,version FROM meite_user WHERE user_id=? AND deleted=0
@GetMapping("/optimisticLockUser")
public String optimisticLock(UserEntity userEntity) {
    Long userId = userEntity.getUserId();
    // 标记该线程是否修改成功
    Integer resultCount = 0;
    while (resultCount <= 0) {
        // 1.根据userid 查找到对应的VERION版本号码 获取当前数据的版本号码
        UserEntity dbUserEntity = userMapper.selectById(userId);
        if (dbUserEntity == null) {
            return "未查询到该用户";
        }
        // 2.做update操作的时候,放入该版本号码  乐观锁
        userEntity.setVersion(dbUserEntity.getVersion());
        resultCount = userMapper.updateById(userEntity);
    }
    return resultCount > 0 ? "success" : "fail";
}

公平与非公平锁

公平锁:就是比较公平,根据请求锁的顺序排列,先来请求的就先获取锁,后来获取锁就最后获取到, 采用队列存放 类似于吃饭排队。

非公平锁:不是根据根据请求的顺序排列, 通过争抢的方式获取锁。

非公平锁效率是公平锁效率要高。Synchronized是非公平锁

New ReentramtLock()(true)---公平锁

New ReentramtLock()(false)---非公平锁

public class MayiktLock implements Runnable {
    private static int count = 0;
    private static Lock lock = new ReentrantLock(true);

    @Override
    public void run() {

        while (count < 200) {
//            try {
//                Thread.sleep(100);
//            } catch (Exception e) {
//
//            }
//            lock.lock();
//            System.out.println(Thread.currentThread().getName() + ",count:" + count);
//            count++;
            createCount();
//            lock.unlock();
        }
    }

    public synchronized void createCount() {
        System.out.println(Thread.currentThread().getName() + ",count:" + count);
        count++;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new MayiktLock()).start();
        }
    }
}

独占锁与共享锁

独占锁:在多线程中,只允许有一个线程获取到锁,其他线程都会等待。

共享锁:多个线程可以同时持有锁,例如ReentrantLock读写锁。读读可以共享、

写写互斥、读写互斥、写读互斥。

public class MyTask extends Thread {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read() {
        lock.readLock().lock();
        System.out.println(">>>" + Thread.currentThread().getName() + ",正在读取锁开始");
        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }
        System.out.println(">>>" + Thread.currentThread().getName() + ",正在读取锁结束");
        lock.readLock().unlock();
    }

    public void write() {
        lock.writeLock().lock();
        System.out.println(">>>" + Thread.currentThread().getName() + ",正在写入锁开始");
        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }
        System.out.println(">>>" + Thread.currentThread().getName() + ",正在写入锁结束");
        lock.writeLock().unlock();
    }

    public static void main(String[] args) {
        MyTask myTask = new MyTask();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> myTask.read()).start();
        }
        for (int i = 0; i < 10; i++) {
            new Thread(() -> myTask.write()).start();
        }
//        lock.writeLock()
    }

}

锁的可重入性

在同一个线程中锁可以不断传递的,可以直接获取。

Cas(自旋)无锁机制

CAS: Compare and Swap,翻译成比较并交换。 执行函数CAS(V,E,N)

CAS有3个操作数,内存值V,旧的预期值E,要修改的新值N。当且仅当预期值E和内存值V相同时,将内存值V修改为N,否则什么都不做。

图片.png
  1. Cas 是通过硬件指令,保证原子性

  2. Java是通过unsafe jni技术

  3. 原子类: AtomicBoolean,AtomicInteger,AtomicLong 等使用 CAS 实现。

使用AtomicInteger实现自增

public class Test002 extends Thread {
    private static AtomicLong atomicLong = new AtomicLong();

    @Override
    public void run() {
        while (atomicLong.get() < 10000) {
            long l = atomicLong.incrementAndGet();
            System.out.println(Thread.currentThread().getName() + "," + l);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Long startTime=System.currentTimeMillis();
        Test002 t1 = new Test002();
        Test002 t2 = new Test002();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        Long endTime=System.currentTimeMillis();
        System.out.println(endTime-startTime);
    }
}
public final long getAndAddLong(Object var1, long var2, long var4) {
    long var6;
    do {
        var6 = this.getLongVolatile(var1, var2);
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

    return var6;
}

Var6 就是为旧的预期值:0 E

Var1,var2 就是我们共享变量值:V

Var6+Var4 就是我们的新值N

如果Var6(E)==Var1,var2 (V) 则修改V为N;

Cas Aba的问题

Cas主要检查 内存值V与旧的预值值=E是否一致,如果一致的情况下,则修改。

这时候会存在ABA的问题:

如果将原来的值A,改为了B,B有改为了A 发现没有发生变化,实际上已经发生了变化,所以存在Aba问题。

解决办法:通过版本号码,对每个变量更新的版本号码做+1

解决aba问题是否大:概念产生冲突,但是不影响结果,换一种方式 通过版本号码方式。

图片.png

AtomicMarkableReference

(1)第一个参数expectedReference:表示预期值。

(2)第二个参数newReference:表示要更新的值。

(3)第三个参数expectedStamp:表示预期的时间戳。

(4)第四个参数newStamp:表示要更新的时间戳。

public class Test004 {
    // 注意:如果引用类型是Long、Integer、Short、Byte、Character一定一定要注意值的缓存区间!
    // 比如Long、Integer、Short、Byte缓存区间是在-128~127,会直接存在常量池中,而不在这个区间内对象的值则会每次都new一个对象,那么即使两个对象的值相同,CAS方法都会返回false
    // 先声明初始值,修改后的值和临时的值是为了保证使用CAS方法不会因为对象不一样而返回false
    private static final Integer INIT_NUM = 1000;
    private static final Integer UPDATE_NUM = 100;
    private static final Integer TEM_NUM = 200;
    private static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(INIT_NUM, 1);

    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                Integer value = (Integer) atomicStampedReference.getReference();
                int stamp = atomicStampedReference.getStamp();
                System.out.println(Thread.currentThread().getName() + " : 当前值为:" + value + " 版本号为:" + stamp);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // value 旧值 内存中的值   UPDATE_NUM 修改的值
                if (atomicStampedReference.compareAndSet(value, UPDATE_NUM, 1, stamp + 1)) {
                    System.out.println(Thread.currentThread().getName() + " : 当前值为:" + atomicStampedReference.getReference() + " 版本号为:" + atomicStampedReference.getStamp());
                } else {
                    System.out.println("版本号不同,更新失败!");
                }
            }
        }, "线程A").start();

    }
}

利用原子类手写CAS无锁

public class AtomicTryLock {

    /**
     * 定义AtomicInteger  修改为1表示该锁已经被使用该 修改为0表示为被使用
     */
    private volatile AtomicInteger atomicInteger = new AtomicInteger(0);
    private Thread lockCurrentThread;

    /**
     * 尝试获取锁
     *
     * @return
     */
    public boolean tryLock() {
        boolean result = atomicInteger.compareAndSet(0, 1);
        if (result) {
            lockCurrentThread = Thread.currentThread();
        }
        return result;
    }

    /**
     * 释放锁
     *
     * @return
     */
    public boolean unLock() {
        if (lockCurrentThread != null && lockCurrentThread != Thread.currentThread()) {
            return false;
        }
        return atomicInteger.compareAndSet(1, 0);
    }

    public static void main(String[] args) {
        AtomicTryLock atomicTryLock = new AtomicTryLock();
        IntStream.range(1, 10).forEach((i) -> new Thread(() -> {

            try {
                boolean result = atomicTryLock.tryLock();
                if (result) {
                    System.out.println(Thread.currentThread().getName() + ",获取锁成功~");
                } else {
                    System.out.println(Thread.currentThread().getName() + ",获取锁失败~");
                }
            } catch (Exception e) {
                e.printStackTrace();
                atomicTryLock.unLock();
            } finally {
                atomicTryLock.unLock();
            }

        }).start());
    }


}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 开篇闲扯 打工人,你是不是也不喜欢吃挂面?吃多了面试容易挂!咔~~好冷的段子。分享一个小故事,中午我对象聊天,她说...
    罗拉快跑跑跑跑阅读 424评论 0 0
  • 乐观锁与悲观锁 乐观与悲观的区别跟读写无关,区别在于对数据操作时加锁操作的差异,数据操作包括(读,写,删等); -...
    Mrsimple_4f84阅读 851评论 0 1
  • Java中有各式各样的锁,大致可以分为以下几类 公平锁 / 非公平锁 可重入锁 互斥锁 / 共享锁 乐观锁 / 悲...
    Hogwarts1024阅读 1,120评论 0 0
  • 锁汇总 锁大致可以分类为1、公平锁 / 非公平锁2、可重入锁 / 非可重入锁3、独享锁 / 共享锁4、乐观锁 / ...
    技术灭霸阅读 755评论 0 0
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 131,137评论 2 7

友情链接更多精彩内容