• 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为每个线程分配一个锁,而不是为每次调用分配一个锁。

  • 相关阅读:
    R: 聚类分析
    R: 主成分分析 ~ PCA(Principal Component Analysis)
    R: 关于 ggplot2 的初探
    R: 字符串处理包:stringr
    R: 用 R 查看、管理文件(夹)
    R: 关于文件 文件夹的处理:file.show() dir.create().....
    R: 绘图 pie & hist
    R: 绘图 barplot
    R: 对向量中的每个元素,检查其是否包含某个“单词”
    R: 一页显示多张图的方法
  • 原文地址:https://www.cnblogs.com/suxuan/p/4948748.html
Copyright © 2020-2023  润新知