锁的分类
悲观锁
悲观锁比较悲观,当多个线程对同一行数据实现修改的时候,最后只有一个线程才能修改成功,只要谁能够对获取该到行锁则其他线程时不能够对该数据做任何修改操作,且是阻塞状态。 比如for update 或者事务开启了事务但是没有提交事务。
select * from information_schema.innodb_trx t
select t.trx_mysql_thread_id from information_schema.innodb_trx t
kill 768; --清理未提交的事务
乐观锁
乐观锁比较乐观,通过预值或者版本号比较,如果不一致性的情况则通过循环控制修改,当前线程不会被阻塞,是乐观,效率比较高。
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"; } |
公平与非公平锁
公平锁:就是比较公平,根据请求锁的顺序排列,先来请求的就先获取锁,后来获取锁就最后获取到, 采用队列存放 类似于吃饭排队。
非公平锁:不是根据根据请求的顺序排列, 通过争抢的方式获取锁。
New ReentramtLock()(true)---公平锁
New ReentramtLock()(false)---非公平锁
![](https://i-blog.csdnimg.cn/blog_migrate/91175aa1e29aff55019213956b2d39bf.png)
非公平锁效率是公平锁效率要高。Synchronized是非公平锁
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
有
3
个操作数:内存值
V
、预期值
A
、要修改的新值
B
。当且仅当预期值
A
和
内存值
V
相同时,将内存值
V
修改为
B
,否则什么都不做
Cas主要检查 内存值V与旧的预值值=E是否一致,如果一致的情况下,则修改。
这时候会存在ABA的问题:
如果将原来的值A,改为了B,B有改为了A 发现没有发生变化,实际上已经发生了变化,所以存在Aba问题。
解决办法:通过版本号码,对每个变量更新的版本号码做+1
Atomic原子类就是用的cas
每一行代码都有它的涵义,多问一句为什么;别怕,理清思路,一切代码都是数据的流动和转化,耐心一点,慢慢积累!一起加油!!!