精华内容
下载资源
问答
  • 可重入锁和不可重入锁的区别

    万次阅读 2020-10-22 21:40:16
    可重入锁示例(同一个线程不可以重入上锁后的代码段) 如下是一个不可重入锁的逻辑过程,会发现执行main方法控制台会打印执行doJob方法前,然后就会一直线程阻塞,不会打印执行doJob方法过程中,原因在于第一次上锁...

    不可重入锁示例(同一个线程不可以重入上锁后的代码段)

    如下是一个不可重入锁的逻辑过程,会发现执行main方法控制台会打印执行doJob方法前,然后就会一直线程阻塞,不会打印执行doJob方法过程中,原因在于第一次上锁后,由于没有释放锁,因此执行第一次lock后isLocked = true,这个时候调用doJob()内部又一次调用了lock()由于上个线程将isLocked = true,导致再次进入的时候就进入死循环。导致线程无法执行System.out.println("执行doJob方法过程中");这行代码,因此控制台只能打印执行doJob方法前。这种现象就造成了不可重入锁

    public class Count{
        MyLock lock = new MyLock();
    
        public static void main(String[] args) throws InterruptedException {
            new Count().doSomeThing(); // 示例的main方法
        }
        public void doSomeThing() throws InterruptedException {
            lock.lock(); // 第一次上锁
            System.out.println("执行doJob方法前");
            doJob(); // 方法内会再次上锁
            lock.unlock(); // 释放第一次上的锁
        }
        public void doJob() throws InterruptedException {
            lock.lock();
            System.out.println("执行doJob方法过程中");
            lock.unlock();
        }
    }
    
    /**
     * 自定义锁
     */
    class MyLock{
        private boolean isLocked = false;
        public synchronized void lock() throws InterruptedException{
            while(isLocked){
                wait();
            }
            isLocked = true; // 线程第一次进入后就会将器设置为true,第二次进入是就会由于where true进入死循环
        }
        public synchronized void unlock(){
            isLocked = false;   // 将这个值设置为false目的是释放锁
            notify();           // 接触阻塞
        }
    }
    

    可重入锁示例(同一个线程可以重入上锁的代码段,不同的线程则需要进行阻塞)

    java的可重入锁有:ReentrantLock(显式的可重入锁)synchronized(隐式的可重入锁)

    可重入锁诞生的目的就是防止上面不可重入锁的那种情况,导致同一个线程不可重入上锁代码段。

    目的就是让同一个线程可以重新进入上锁代码段。

    设计可重入锁的示例代码

    public class MyReentrantLock {
        boolean isLocked = false;   // 默认没有上锁
        Thread lockedBy = null; // 记录阻塞线程
        int lockedCount = 0;    // 上锁次数计数
    
        /**
         * 上锁逻辑
         */
        public synchronized void lock() throws InterruptedException {
            Thread thread = Thread.currentThread();
            // 上锁了 并且 如果是同一个线程则放行,否则其它线程需要进入where循环进行等待
            while (isLocked && lockedBy != thread) { 
                wait();
            }
            isLocked = true; // 第一次进入就进行上锁
            lockedCount++; // 上锁次数计数
            lockedBy = thread; // 当前阻塞的线程
        }
    
        /**
         * 释放锁逻辑
         */
        public synchronized void unlock() {
            if (Thread.currentThread() == this.lockedBy) {
                lockedCount--; // 将上锁次数减一
                if (lockedCount == 0) {// 当计数为0,说明所有线程都释放了锁
                    isLocked = false; // 真正的将释放了所有锁
                    notify();
                }
            }
        }
    }
    
    展开全文
  • 可重入锁

    2020-11-19 14:08:41
    可重入锁又名递归锁 是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象), 不会因为之前已经获取过还没释放而阻塞。 Java中ReentrantLock和synchronized...
     
    可重入锁又名递归锁
     
    是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),
    不会因为之前已经获取过还没释放而阻塞。
     
    Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。
    
    可重入锁种类
    
    隐式锁(即synchronized关键字使用的锁)默认是可重入锁
    
    package com.hhf.study.juc;
    
    /**
     * 可重入锁:可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
     *
     * 在一个synchronized修饰的方法或代码块的内部
     * 调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的
     */
    
    public class ReEnterLockDemo {
    
        static Object objectLockA = new Object();
    
        public static void m1(){
            new Thread(() -> {
                synchronized (objectLockA){
                    System.out.println(Thread.currentThread().getName()+"\t"+"------外层调用");
                    synchronized (objectLockA){
                        System.out.println(Thread.currentThread().getName()+"\t"+"------中层调用");
                        synchronized (objectLockA)
                        {
                            System.out.println(Thread.currentThread().getName()+"\t"+"------内层调用");
                        }
                    }
                }
            },"t1").start();
    
        }
    
        public static void main(String[] args) {
            m1();
        }
    }
     
     
    
    package com.hhf.study.juc;
    
    /**
     * 可重入锁:可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
     *
     * 在一个synchronized修饰的方法或代码块的内部
     * 调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的
     */
    
    public class ReEnterLockDemo {
    
        public synchronized void m1(){
            System.out.println("=====外层");
            m2();
        }
    
        public synchronized void m2() {
            System.out.println("=====中层");
            m3();
        }
    
        public synchronized void m3(){
            System.out.println("=====内层");
        }
    
    
        public static void main(String[] args) {
            new ReEnterLockDemo().m1();
        }
    }
     
     
    
    

    Synchronized的重入的实现机理

     
    每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。
     
    当执行monitorenter时,如果目标锋对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设
    置为当前线程,并且将其计数器加i。
     
    在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待
    ,直至持有线程释放该锁。
     
    当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。
    

    显式锁(即Lock)也有ReentrantLock这样的可重入锁。

    package com.hhf.study.juc;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 可重入锁:可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
     *
     * 在一个synchronized修饰的方法或代码块的内部
     * 调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的
     */
    
    public class ReEnterLockDemo {
    
        static Lock lock = new ReentrantLock();
    
        public static void main(String[] args) {
            new Thread(() -> {
                lock.lock();
                //lock.lock();
                    try{
                        System.out.println("=======外层");
                           lock.lock();
                           try{
                               System.out.println("=======内层");
                           }finally {
                              lock.unlock();
                           }
                    } finally {
                        //实现加锁次数和释放次数不一样
                        //由于加锁次数和释放次数不一样,第二个线程始终无法获取到锁,导致一直在等待。
                        lock.unlock();
                        //lock.unlock();    //正在情况,加锁几次就要解锁几次
                    }
            },"t1").start();
    
            new Thread(() -> {
                    lock.lock();
                    try{
                        System.out.println("b thread----外层调用lock");
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                      lock.unlock();
                    }
            },"b").start();
    
    
        }
    }
     
     
    

     

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,936
精华内容 2,774
关键字:

可重入锁