一起看看synchronized的4个特性

互联网 20-7-7

1. synchronized锁重入

1.1 介绍

例如:

public class Service1 {      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 class MyThread extends Thread {      @Override     public void run(){         Service1 service1 = new Service1();         service1.method1();     }       public static void main(String[] args) {         MyThread myThread = new MyThread();         myThread.start();     } }

运行结果如下: ☹ 看到这个结果的时候是茫然了,怎么就证明是可重入锁的了呢? ➤ “可重入锁”的概念是:自己可以再次获取自己的内部锁,比如有1个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。 ➤ “可重入锁”的最大作用就是避免死锁

1.2 分析

我们知道,在程序中,是无法显式释放对同步监视器的锁的,而会在如下几个情况下释放锁: ① 当前线程的同步方法、代码块执行结束的时候释放 ② 当前线程在同步方法、同步代码块遇到break、return终止该代码块或方法的时候释放 ③ 出现未处理的error或exception导致异常结束的时候释放 ④ 程序执行了同步对象wait方法,当前线程暂停,释放锁

那么,在上面的程序中,当线程进入同步方法method1时获得Service1的对象锁,但是在执行method1的时候调用了同步方法method2,按照正常情况,在执行同步方法method2同样需要获得对象锁,但是根据上面释放锁的条件,此时method1的对象锁还没有释放,此时就会造成死锁,无法继续执行method2。但是通过上面代码的执行结果来看,能够正常执行method2和method3,这就说明,在一个Synchronized修饰的方法或代码块的内部调用本类的其他Synchronized修饰的方法或代码块时,是永远可以得到锁的

1.3 父子可继承性

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

public class Service2 {     public int i = 10;     public synchronized void mainMethod(){         i--;         System.out.println("main print i="+i);         try {             Thread.sleep(100);         } catch (InterruptedException e) {             e.printStackTrace();         }     } }
public class Service3 extends Service2 {      public synchronized void subMethod(){         try{             while (i>0){                 i--;                 System.out.println("sub print i= "+i);                 Thread.sleep(100);                 this.mainMethod();             }         }catch (InterruptedException e){             e.printStackTrace();         }     } }
public class MyThread extends Thread {      @Override     public void run(){         Service3 service3 = new Service3();         service3.subMethod();     }       public static void main(String[] args) {         MyThread myThread = new MyThread();         myThread.start();     } }

运行结果如下: 此程序说明,当存在父子类继承关系时,子类完全可以通过“可重入锁”调用父类的同步方法。

2. 出现异常,锁自动释放

public class Service4 {      public synchronized void testMethod(){         if(Thread.currentThread().getName().equals("a")){             System.out.println("ThreadName= "+Thread.currentThread().getName()+" run beginTime="+System.currentTimeMillis());             int i=1;             while (i == 1){                 if((""+Math.random()).substring(0,8).equals("0.123456")){                     System.out.println("ThreadName= "+Thread.currentThread().getName()+" run exceptionTime="+System.currentTimeMillis());                   //Integer.parseInt("a");                 }             }         }else{             System.out.println("Thread B run time= "+System.currentTimeMillis());         }     } }
public class ThreadA extends Thread{      private Service4 service4;      public ThreadA(Service4 service4){         this.service4 = service4;     }      @Override     public void run(){         service4.testMethod();     } }
public class ThreadB extends Thread{      private Service4 service4;      public ThreadB(Service4 service4){         this.service4 = service4;     }      @Override     public void run(){         service4.testMethod();     } }
public class Main {     public static void main(String[] args) {         try {             Service4 service4 = new Service4();              ThreadA a = new ThreadA(service4);             a.setName("a");             a.start();              Thread.sleep(500);              ThreadB b = new ThreadB(service4);             b.setName("b");             b.start();         } catch (InterruptedException e) {             e.printStackTrace();         }     } }

注意Service4类中Integer.parseInt(“a”);此时处于被注释状态,运行结果如下: 由于a线程没有错误,while(true),此时a线程处于无限循环状态,锁一直被a占用,b线程无法获得锁,即无法执行b线程。

将Service4类中Integer.parseInt(“a”);解开注释,执行的结果如下:

   当a线程发生错误时,b线程获得锁从而执行,由此可见,当方法出现异常时,锁自动释放。

3. 将任意对象作为监视器

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) {         final StringLock stringLock = new StringLock();         new Thread(new Runnable() {             @Override             public void run() {                 stringLock.method();             }         },"t1").start();          new Thread(new Runnable() {             @Override             public void run() {                 stringLock.method();             }         },"t2").start();     } }

运行结果如下: 锁非this对象具有一定的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不予其他锁this同步方法争抢this锁,则可大大提高运行效率。

4. 同步不具有继承性

父类的同步方法,在子类中重写后不加同步关键字,是不会同步的,所以还得在子类的方法中添加synchronized关键字。

推荐学习:Java视频教程

以上就是一起看看synchronized的4个特性的详细内容,更多内容请关注技术你好其它相关文章!

来源链接:
免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表本站的观点或立场
上一篇:php获取远程图片并下载保存到本地的方法分析 下一篇:java中get请求中文乱码怎么办?

相关资讯