zoukankan      html  css  js  c++  java
  • Java并发编程可重入锁

    可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍可以获取该锁而不受影响。
    在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁。

    public class Test implements Runnable{
        public synchronized void get(){
            System.out.println(Thread.currentThread().getId());
            set();
        }
    
        public synchronized void set(){
            System.out.println(Thread.currentThread().getId());
        }
    
        @Override
        public void run() {
            get();
        }
        public static void main(String[] args) {
            Test ss=new Test();
            new Thread(ss).start();
            new Thread(ss).start();
            new Thread(ss).start();
        }
    }
    

    两个例子最后的结果都是正确的,即 同一个线程id被连续输出两次。
    结果如下:
    Threadid: 8
    Threadid: 8
    Threadid: 10
    Threadid: 10
    Threadid: 9
    Threadid: 9
    可重入锁最大的作用是避免死锁
    我们以自旋锁作为例子

    public class SpinLock {
        private AtomicReference owner =new AtomicReference<>;
        public void lock(){
            Thread current = Thread.currentThread();
            while(!owner.compareAndSet(null, current)){
            }
        }
        public void unlock (){
             Thread current = Thread.currentThread();
             owner.compareAndSet(current, null);
        }
    }
    

    对于自旋锁来说,
    1、若有同一线程两次调用lock() ,会导致第二次调用lock位置进行自旋,产生了死锁
    说明这个锁并不是可重入的。(在lock函数内,应验证线程是否为已经获得锁的线程)
    2、若1问题已经解决,当unlock()第一次调用时,就已经将锁释放了。实际上不应释放锁。
    (采用计数次进行统计)
    修改之后,如下:

    public class SpinLock1 {
        private AtomicReference owner =new AtomicReference<>;
        private int count =0;
        public void lock(){
            Thread current = Thread.currentThread();
            if(current==owner.get()) {
                count++;
                return ;
            }
    
            while(!owner.compareAndSet(null, current)){
    
            }
        }
    public void unlock (){
        Thread current = Thread.currentThread();
        if(current==owner.get()){
            if(count!=0){
                count--;
            }else{
                owner.compareAndSet(current, null);
            }
    
        }
    
        }
    }
    

    该自旋锁即为可重入锁。

    可重入锁机制:每个锁都关联一个请求计数器和一个占有他的线程,当请求计数器为0时,这个锁可以被认为是unhled的,当一个线程请求一个unheld的锁时,JVM记录锁的拥有者,并把锁的请求计数加1,如果同一个线程再次请求这个锁时,请求计数器就会增加,当该线程退出syncronized块时,计数器减1,当计数器为0时,锁被释放。

    public class Widget {
    public synchronized void doSomething() {
    ...
    }
    }
    
    public class LoggingWidget extends Widget {
        public synchronized void doSomething() {
            System.out.println(toString() + ": calling doSomething");
            super.doSomething();
        }
    }
    

    如果没有Java锁的可重入性,当一个线程获取LoggingWidget的doSomething()代码块的锁后,这个线程已经拿到了LoggingWidget的锁,当调用父类中的doSomething()方法的时,JVM会认为这个线程已经获取了LoggingWidget的锁,而不能再次获取,从而无法调用Widget的doSomething()方法,从而造成死锁。从中我们也能看出,java线程是基于“每线程(per-thread)”,而不是基于“每调用的(per-invocation)”的,也就是说java为每个线程分配一个锁,而不是为每次调用分配一个锁。

  • 相关阅读:
    Swift中的参数内部名称和外部名称
    iOS 发布流程
    解决xcode iOS真机调试正常,模拟器失败问题
    iOS 解决ipv6问题
    cocos2dx 字体描边遇到的描边缺失的bug
    cocos2dx for iOS fmod的音效引擎接入
    skynet 学习笔记-sproto模块(2)
    cocos2dx for android 接入 fmod的过程
    skynet 学习笔记-netpack模块(1)
    linux 安装并且设置环境lua环境变量
  • 原文地址:https://www.cnblogs.com/suxuan/p/4948748.html
Copyright © 2011-2022 走看看