锁的学习笔记

锁的分类

读写锁,非公平锁,可重入锁,自旋锁,乐观锁悲观锁,死锁

读写锁

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();//volatile保证可见性
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
 
    public void put(String key, Object value) {
        rwLock.writeLock().lock();//写锁创建
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);
            try {
                // 模拟网络拥堵,延迟0.3秒
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 写入完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.writeLock().unlock();//写锁释放
        }
    }
 
    public void get(String key) {
        rwLock.readLock().lock();//读锁创建
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在读取:");
            try {
                TimeUnit.MILLISECONDS.sleep(300);//模拟网络拥堵,延迟0.3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Object value = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 读取完成:" + value);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.readLock().unlock();//读锁释放
        }
    }
 
    public void clean() {
        map.clear();//清空缓存
    }
}
 
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
 
        for (int i = 1; i <= 5; i++) {//5个线程写
            final int tempInt = i;//final
            new Thread(() -> {
                myCache.put(tempInt + "", tempInt +  "");
            }, String.valueOf(i)).start();
        }
 
        for (int i = 1; i <= 5; i++) {//5个线程读
            final int tempInt = i;//final
            new Thread(() -> {
                myCache.get(tempInt + "");
            }, String.valueOf(i)).start();
        }
    }
}

非公平锁(synchronized/ReentrantLock)

名字

定义

区别

非公平锁

多个线程获取锁的顺序,并不是按照申请锁的顺序,有可能申请的线程比先申请的线程优先获取到锁,在高并发的环境下,有可能造成优先翻转,或者饥饿的线程(也就是某个线程一直获取不到锁)

比较粗鲁,上来就直接尝试占有锁,如果失败,就采用公平锁的方式

公平锁

多个线程按照申请锁的顺序来获取锁,队列,先进先出先进先获取,这就是公平的

很公平在并发的环境下,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列中的第一个,就占用锁,否者就会加入到等待队列中,以后安装FIFO的规则从队列中取到自己

  • synchronized是非公平锁
  • ReentrantLock默认非公平锁
    • Lock lock = new ReentrantLock(true);//默认false非公平锁,true公平锁

可重入锁/递归锁(synchronized/ReentrantLock)

class MySynchronized {
    public synchronized void sendSMS() throws Exception{//发短信
        System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()");
        sendEmail();//同步方法中调用另外一个同步方法
    }
 
    public synchronized void sendEmail() throws Exception{//发邮件
        System.out.println(Thread.currentThread().getId() + "\t invoked sendEmail()");
    }
}
public class MyDemo {
    public static void main(String[] args) {
        MySynchronized mySynchronized = new MySynchronized();
        new Thread(() -> {
            try {
                mySynchronized.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t1").start();
 
        new Thread(() -> {
            try {
                mySynchronized.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }
}
/**
t1  invoked sendSMS()
t1  invoked sendEmail()
t2  invoked sendSMS()
t2  invoked sendEmail()
*/
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
class MyReentrantLock implements Runnable{
    Lock lock = new ReentrantLock();
 
    @Override
    public void run() {
        method1();
    }
 
    public void method1() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() 
            + "\t exe method1");
            method2();
        } finally {
            lock.unlock();
        }
    }
 
    public void method2() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() 
            + "\t exe method2");
        } finally {
            lock.unlock();
        }
    }
}
public class ReenterLockDemo {
    public static void main(String[] args) {
        MyReentrantLock myReentrantLock = new MyReentrantLock();
        Thread t1 = new Thread(myReentrantLock, "t1");
        Thread t2 = new Thread(myReentrantLock, "t2");
        t1.start();
        t2.start();
    }
}
/**
t1  exe method1
t1  exe method2
t2  exe method1
t2  exe method2
*/

自旋锁(spinlock)

public class SpinLockDemo {
    // 现在的泛型装的是Thread,原子引用线程
    AtomicReference<Thread>  atomicReference = new AtomicReference<>();
 
    public void myLock() {//加锁
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t come in ");
        //开始自旋,期望值是null,更新值是当前线程,如果是null,则更新为当前线程,否者自旋
        while(!atomicReference.compareAndSet(null, thread)) {
        }
    }
 
    public void myUnLock() {//解锁
        Thread thread = Thread.currentThread();
        //自己用完了后,把atomicReference变成null
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock()");
    }
 
    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            spinLockDemo.myLock();//加锁
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnLock();//释放锁
        }, "t1").start();
 
        //1秒后,启动t2线程占用锁
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            spinLockDemo.myLock();//加锁
            spinLockDemo.myUnLock();//释放锁
        }, "t2").start();
    }
}
/**
t1   come in
.....五秒后.....
t1   invoked myUnlock()
t2   come in 
t2   invoked myUnlock()
*/

乐观锁/悲观锁

MybatisPlus使用乐观锁的3步走

step1、在数据库增加version字段,默认为1

step2、在实体类增加对应的字段

@Version

private Integer version;

step3、注册乐观锁,在MybatisPlusConfig中配置

@Bean

public OptimisticLockerInterceptor optimisticLockerInterceptor() {

return new OptimisticLockerInterceptor();

}

死锁

import java.util.concurrent.TimeUnit;
 
class HoldLockThread implements Runnable{
    private String lockA;
    private String lockB;
 
    public HoldLockThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }
 
    @Override
    public void run() {
        synchronized (lockA) {
            System.out.println(Thread.currentThread().getName() 
            + "\t 自己持有" + lockA + "\t 尝试获取:" + lockB);
 
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
 
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() 
                + "\t 自己持有" + lockB + "\t 尝试获取:" + lockA);
            }
        }
    }
}
 
public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        new Thread(new HoldLockThread(lockA, lockB), "t1").start();
        new Thread(new HoldLockThread(lockB, lockA), "t2").start();
    }
}
/**
t1   自己持有lockA   尝试获取:lockB
t2   自己持有lockB   尝试获取:lockA
*/

死锁的检测

sychronized与lock的对比

sychronized

lock

1.定义

JVM层面的java关键字,底层是通过monitor对象来完成

api层面的锁,底层是JUC锁(java.util.concurrent.locks.Lock)

2.使用方法

不需要用户去手动释放锁,系统自动释放

需要用户去手动释放锁,若没有主动释放锁,就有可能出现死锁的现象,需要lock() 和 unlock() 配置try catch语句来完成

3.等待是否中断

不可中断


可中断,可以设置超时方法

  • 设置超时方法,trylock(long timeout, TimeUnit unit)
  • lockInterrupible() 放代码块中,调用interrupt() 方法可以中断

4.加锁是否公平

非公平锁

默认非公平锁,构造函数可以传递boolean值,true为公平锁,false为非公平锁

锁绑定多个条件Condition

没有,要么随机,要么全部唤醒

可以精确唤醒

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值