锁机制

Posted tinghao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了锁机制相关的知识,希望对你有一定的参考价值。

1.锁是干什么用的
  锁一般来说用作资源控制,限制资源访问,防止在并发环境下造成数据错误

2.重入锁
  重入锁也叫作递归锁,指的是同一个线程外层函数获取到一把锁后,内层函数同样具有这把锁的控制权限
  synchronized和ReentrantLock就是重入锁对应的实现
  synchronized重量级的锁 
  ReentrantLock轻量级的锁 lock()代表加入锁 unlock()代表释放锁

  不可重入锁:说明当没有释放该锁时。其他线程获取该锁会进行等待

 

技术图片
public class MyLock {
    //标识锁是否可用  如果值为 true代表有线程正在使用该 锁 ,如果为false代表没有人使用锁
    private boolean isLocked=false;
    //获取锁:加锁
    public synchronized void lock() throws InterruptedException {
        //判断当前该锁是否正在使用
        while (isLocked){
            wait();
        }
        //当前没有人使用情况 下就占用该锁
        isLocked=true;
    }

    //释放锁
    public synchronized  void unLock(){
        //将当前锁资源释放
        isLocked=false;
        //唤起正在等待使用锁的线程
        notify();
    }

}
技术图片

 

技术图片
public class MyLockTest {
MyLock myLock=new MyLock();
    //A业务
    public void print() throws InterruptedException {
        //获取一把锁
        myLock.lock();
        System.out.println("print业务方法");
        doAdd();
        //释放锁
        myLock.unLock();
    }
    //B业务方法
    public void  doAdd() throws InterruptedException {
        //获取一把锁
        myLock.lock();
        System.out.println("aoAdd方法");
        //释放锁
        myLock.unLock();

    }

    public static void main(String[] args) throws InterruptedException {
        MyLockTest myLockTest=new MyLockTest();
        myLockTest.print();
    }
}
技术图片

 

  控制台结果:

当前效果就造成了死锁

技术图片

 

 

 

  synchronized可重入性:如果当前A持有一把锁,在A业务内部调用B,那么B也同样拥有这把锁的使用权限

  编写测试代码:

技术图片
MyLock myLock=new MyLock();
    //A业务
    public synchronized void print() throws InterruptedException {

        System.out.println("print业务方法");
        doAdd();

    }
    //B业务方法
    public synchronized void  doAdd() throws InterruptedException {

        System.out.println("aoAdd方法");


    }

    public static void main(String[] args) throws InterruptedException {
        MyLockTest myLockTest=new MyLockTest();
        myLockTest.print();
    }
技术图片

  控制台结果:

技术图片

 

 

 

  ReentrantLock同样具有可重入性

  编写测试代码:

技术图片
public class MyLockTest {
    //创建锁对象
    Lock lock=new ReentrantLock();
    //A业务
    public void print() throws InterruptedException {
        //获取了一把锁
        lock.lock();
        System.out.println("print业务方法");
        doAdd();
        //释放锁
        lock.unlock();

    }
    //B业务方法
    public void  doAdd() throws InterruptedException {
        //获取一把锁
        lock.lock();
        System.out.println("aoAdd方法");
        //释放锁
        lock.unlock();

    }

    public static void main(String[] args) throws InterruptedException {
        MyLockTest myLockTest=new MyLockTest();
        myLockTest.print();
    }
}
技术图片

  控制台结果:

技术图片

 

 

 

3. 读写锁

  并发线程下,所有线程都执行读的操作,会不会有问题
  并发线程下,部分读部分写会不会有问题 会发生写冲突
  并发线程下,所有线程都执行写会不会有问题 会发生写冲突

 

  编写测试代码:

技术图片
 //创建一个集合
    static Map<String,String> map=new HashMap<String,String>();
    //创建一个读写锁
    static ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
    //获取读锁
    static Lock readLock=lock.readLock();
    //获取写锁
    static Lock writeLock=lock.writeLock();
    //写操作
    public Object put(String key,String value){
        writeLock.lock();
        try {
            System.out.println("Write正在执行写操作~");
            Thread.sleep(100);
            String put = map.put(key, value);
            System.out.println("Write写操作执行完毕~");
            return put;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            writeLock.unlock();
        }
        return null;

    }

    //写操作
    public Object get(String key){
        readLock.lock();
        try {
            System.out.println("Read正在执行读操作~");
            Thread.sleep(100);
            String value = map.get(key);
            System.out.println("Read读操作执行完毕~");
            return value;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            readLock.unlock();
        }
        return null;

    }

    public static void main(String[] args) {
        ReadWriteLock lock=new ReadWriteLock();
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(()->{
                try {
                    //写操作
                    lock.put(finalI +"","value"+finalI);
                    //读操作
                    System.out.println(lock.get(finalI+""));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }

    }
技术图片

  控制台 结果:

我们可以看出是当所有写操作都执行完毕后才开始执行读操作

技术图片

 

4. 乐观锁

  总认为不会发生并发问题,每一次取数据时总认为其他线程不会对该数据先进性更改,但是在更新时会判断其他线程在这之前有

  没有对该数据进行修改,
  数据库当中常用方案:版本号控制

5.悲观锁
  总是假设最坏的情况,每次取数据时,都会认为其他线程会对该数据进行修改,所以会进行加锁
  其他线程访问的时候会阻塞等待,例如在数据库当中可以使用行锁,表锁以及读写锁等方式实现

以上是关于锁机制的主要内容,如果未能解决你的问题,请参考以下文章

Redis实现分布式锁(设计模式应用实战)

JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段

锁机制-操作系统以及汇编代码层面

锁机制,信号量机制,事件机制

java中锁机制

Java锁机制总结