第五章 Synchronized的性质

一、可重入
1,指的是同一个线程的外层函数(synchornized)获得锁之后,内层函数(synchornized)可以再次获取该锁。
2,线程t1拿到了Monitor,想再次使用Monitor进入下一个同步方法,如果要释放掉Monitor,然后重新和其他线程竞争Monitor就是不可重入;如果无需提前释放Monitor,直接拿着Monitor去用,就是可重入
3,可重入锁也称为递归锁。
4,在java中,有两个可重入锁,一个就是synchornized关键字,还有一个就是ReentrantLock.
5,可重入锁的好处: 避免死锁,提升封装性,避免了一次次的解锁加锁。
为什么能避免死锁呢?
假设synchornized不具备可重入性,t1进入一个synchornized方法1获取到了Monitor,然后又要进入下一次synchornized方法2,这个时候synchornized方法1没有执行完所以是不会释放锁的,这个时候因为不具备可重入性,所以进入synchornized方法2又要求重新获取Monitor,这样就造成了死锁。
所以synchornized的可重入性可以避免线程永远等待下去的现象。

6,可重入的scope:
java的synchornized关键字默认加锁的范围是线程,但是linux中的pthread,它互斥体的默认加锁行为是以调用为粒度的。

线程而非调用(用三种情况来说明和pthread的区别)
情况1:证明同一个方法是可重入的

package synchornized;

/**
 * Synchronized 可重入粒度测试:
 * 证明同一个方法是可重入的
 */
public class SynchronizedRecursion10 implements Runnable{
    int num = 0;
    public static void main(String[] args) throws InterruptedException {
        SynchronizedRecursion10 runnable = new SynchronizedRecursion10();
        Thread thread0 = new Thread(runnable);
        thread0.start();
        thread0.join();
    }
    
    private synchronized void syn(){
        System.out.println("get monitor");
        num++;
        if (num==3){
            return;
        }else {
            syn();
        }
    }
    
    @Override
    public void run() {
        syn();
    }
}

情况2:证明可重入不要求是同一个方法

package synchornized;

/**
 * Synchronized 可重入粒度测试:
 * 证明可重入不要求是同一个方法
 */
public class SynchronizedRecursion11 implements Runnable{
    int num = 0;
    public static void main(String[] args) throws InterruptedException {
        SynchronizedRecursion11 runnable = new SynchronizedRecursion11();
        Thread thread0 = new Thread(runnable);
        thread0.start();
        thread0.join();
    }
    
    private synchronized void syn1(){
        System.out.println("enter syn1");
        syn2();
    }
    
    private synchronized void syn2(){
        System.out.println("enter syn2");
    }
    
    @Override
    public void run() {
        syn1();
    }
}

情况3:证明可重入不要求是同一个类中的方法:子类调用父类的方法

package synchornized;

/**
 * Synchronized 可重入粒度测试:
 * 证明可重入不要求是同一个类中的方法:子类调用父类的方法
 */
public class SynchronizedRecursionSuperClass12 {
    public synchronized void syn2(){
        System.out.println("我是父类");
    }
}
class SynchronizedRecursion12 extends SynchronizedRecursionSuperClass12 implements Runnable{
    
    public static void main(String[] args) throws InterruptedException {
        
        SynchronizedRecursion12 runnable = new SynchronizedRecursion12();
        Thread thread0 = new Thread(runnable);
        thread0.start();
        thread0.join();
    }
    @Override
    public synchronized void syn2(){
        System.out.println("我是子类");
        super.syn2();
    }
    
    @Override
    public void run() {
        syn2();
    }
}

synchornized关键字的粒度肯定不是调用层面的,是线程级别的。在同一个线程中,它如果已经拿到了一把锁,然后又想接着使用这把锁去访问其他方法,只要该方法需要的锁依然是我手中的这把锁,那么它的可重入的性质就会被激发出来,就不需要显式地释放锁再重新获取锁,它可以直接拿着锁去完成访问。

二,不可中断
一旦这个锁已经被别人获得了,如果我还想获得,我只能选择等待或者阻塞,直到别的线程释放这个锁。如果别人永远不释放该锁,那么我只能永远地等待下去。
相比之下,在未来会介绍的Lock类,可以拥有中断的能力,第一点,如果我觉得我等的时间太长了,有权中断现在已经获取到锁的线程的执行;第二点,如果我觉得我等待的时间太长了不想再继续等了,也可以退出。
Lock相对于Synchronized更加灵活,但是在编码的时候注意点也更多。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容