Java多线程编程-(2)-可重入锁和Synchronized的其他基本特性

一、Synchronized锁重入

       1、Synchronized关键字拥有所锁重入功能,也就是在使用Synchronized的时候,当一个线程获得一个对象的锁之后,在该锁里执行代码时再次请求该对象的锁,可以再次获得该对象的锁。也就是说当线程请求一个由其他线程持有的锁时,该线程会阻塞,而线程请求由自己持有的锁时,如果该锁是重入锁,请求就会成功,否则会阻塞。

       2、一个简单的例子就是:在一个Synchronized修饰的方法或代码块的内部调用本类的其他Synchronized修饰的方法或代码块时,是永远可以得到锁的。实例代码A如下:

public class SyncDubbo{

    public synchronized void method1(){

        System.out.println("method1----------");

        method2();

    }

    public synchronized void method2(){

        System.out.println("method2----------")

        method3();

    }

    public synchronized void method3(){

         System.out.println("method3----------");

    }

    public static void main(String[] args){

        SyncDubbo syncDubbo = new SyncDubbo();

        new Thread(new Runnable(){

             public void run(){

                  syncDubbo.method1();

             }

        }).start();

    }

}

以上代码演示了如何在一个已经被synchronized关键字修饰过的方法再去调用对象中其他被synchronized修饰的方法。


那么为什么要引入重入锁这种机制呢?  一个对象一把锁,多个对象多把锁,重入锁的概念就是:自己可以获取自己的内部锁。

假如有1个对象T获得了对象A的锁,那么该线程T如果在未释放前再次请求该对象的锁时,如果没有可重入锁的机制,是不会获取到锁的,这样的话就会出现死锁的情况。

就如代码A体现的那样,线程T在执行到method1()内部的时候,由于线程已经获取了该对象的syncDubbo的对象锁,当执行到method2()的时候,会再次请求该对象的对象锁,如果内有可重入锁的机制的话,由于该线程T还未释放在刚进入method1()时获取的对象锁,当执行到method2()的时候就会出现死锁。

可重入锁到底有什么用?正如上述代码A和上一段中解释的那样,重入锁最大的作用就是避免死锁。假如有一个场景:用户名和密码保存在本地的txt文件中,则登录验证方法和更新密码的方法都应该被加载synchronized,那么当更新密码的时候需要验证密码的合法性,所以需要调用验证方法,此时是可以调用的。

重入锁的实现原理可以参考下面的网址:http://www.cnbolgs.com/pureEve/p/6421273.html进行学习。

可重入锁的其他特性:父子可继承性

可重入锁支持在父子类继承的环境中,示例代码如下:

public class syncDubbo{

    static class Main{

         public int i= 5;

         public synchronized void operationSub(){

               i--;

               System.out.println("Main print i = " + i);

               try{

                     Thread.sleep(5000);

                }catch (InterruptedException e){

                     e.printStaticTrace();

                }

         }

    }

    static class Sup extends Main{

          public synchronized void operationSub(){

                while(i > 0){

                     i--;

                     System.out.println("sub print i = " + i);

                    try{

                          Thread.sleep(5000);

                      }catch (InterruptedException e){

                          e.printStaticTrace();

                      }

               }

          }   

    }

  public static void main(String[] args){

           new Thread(new Runnable(){

                  public void run(){

                        Sub sub = new Sub();

                        sub.operationSub();

                  }

           }).start();

  }

}


二、Synchronized的其他特性

1、出现异常时,锁自动释放

就是说,当一个线程执行的代码出现异常时,其所持有的锁会自动释放,示例如下:

public class SyncException {

    private int i = 0;
    
    public synchronized void operation() {
        while(true) {
            i++;
            System.out.println(Thread.currentThread().getName() + " ,i= " + i );
            if(i == 10) {
                Integer.parseInt("a");
            }
        }
        
    }
    
    public static void main(String[] args) {
        SyncException se = new SyncException();
        new Thread(new Runnable() {
            public void run() {
                se.operation();
            }
        },"t1").start();
        
    }

}

执行后可看出当代码报错的时候,程序不会再执行,即释放了锁。

2、将任意对象作为监视器

public class StringLock {

    private String lock = "lock";
    
    public void method() {
        synchronized(lock) {
            
            try {
                System.out.println("当前线程: " + Thread.currentThread().getName() + "开始");
                Thread.sleep(1000);
                System.out.println("当前线程: " + Thread.currentThread().getName() + "结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
        }
        
    }
    
    public static void main(String[] args) {
        
        StringLock sl = new StringLock();
        
        new Thread(new Runnable() {
            public void run() {
                sl.method();
            }
        },"t1").start();
        
        new Thread(new Runnable() {
            public void run() {
                sl.method();
            }
        },"t2").start();
        
    }
    
}

3、单例模式

单例中有一个经典的实现就是:双重锁校验,其中就是用到了synchronized,实现代码如下:

public class DubbleSingleton {

    private static DubbleSingleton instance;
    
    public static DubbleSingleton getInstance() {
        
        if(instance == null) {
            try {
                // 模块初始化对象的准备时间
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            //加上锁,表示当前对象不可以在其他线程的时候创建
            synchronized(DubbleSingleton.class) {
                //如果不加这一曾判断的话,这样的话每一个线程会得到一个实例
                //而不是所有的线程得到的是一个实例
                if(instance == null) {
                    instance = new DubbleSingleton();
                }
            }
        }

        return instance;
    }
}



1、双重锁校验需要将对象声明为violate,不然会因为指令重排序导致第一个判断空时将为初始化的对象返回。






以上内容转载自-----------微信公众号:Java后端技术










你可能感兴趣的:(java线程)