一、synchronized锁重入
1. 概念
关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时是可以再次得到此对象的锁。
2. 示例
【com.xxy.base.sync005.SyncDubbo1】
1 package com.xxy.base.sync005; 2 3 public class SyncDubbo1 { 4 5 public synchronized void method1() { 6 System.out.println("method1..."); 7 method2(); 8 } 9 10 public synchronized void method2() { 11 System.out.println("method2..."); 12 method3(); 13 } 14 15 public synchronized void method3() { 16 System.out.println("method3..."); 17 } 18 19 public static void main(String[] args) { 20 final SyncDubbo1 sd = new SyncDubbo1(); 21 Thread t1 = new Thread(new Runnable() { 22 23 @Override 24 public void run() { 25 sd.method1(); 26 } 27 }); 28 t1.start(); 29 } 30 }
【com.xxy.base.sync005.SyncDubbo2】
1 package com.xxy.base.sync005; 2 3 public class SyncDubbo2 { 4 static class Main{ 5 public int i = 10; 6 public synchronized void operationSup() { 7 try { 8 i--; 9 System.out.println("Main print i = " + i); 10 Thread.sleep(100); 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 } 15 } 16 } 17 18 static class Sub extends Main{ 19 public synchronized void operationSub() { 20 try { 21 while(i > 0) { 22 i--; 23 System.out.println("Sub print i = " + i); 24 Thread.sleep(100); 25 this.operationSup(); 26 } 27 } catch (InterruptedException e) { 28 // TODO Auto-generated catch block 29 e.printStackTrace(); 30 } 31 } 32 } 33 34 public static void main(String[] args) { 35 Thread t1 = new Thread(new Runnable() { 36 37 @Override 38 public void run() { 39 Sub sub = new Sub(); 40 sub.operationSub(); 41 } 42 }); 43 44 t1.start(); 45 } 46 }
3. 出现异常,锁自动释放,示例:【com.xxy.base.sync005.SyncException】
1 package com.xxy.base.sync005; 2 3 public class SyncException { 4 5 private int i = 0; 6 public synchronized void operation() { 7 while(true) { 8 try { 9 i++; 10 Thread.sleep(200); 11 System.out.println(Thread.currentThread().getName() + ", i = " + i); 12 if(i == 10) { 13 Integer.parseInt("a");//throw RuntimeException(NumberFormatException) 14 } 15 } catch (Exception e) {//InterruptedException 16 e.printStackTrace(); 17 System.out.println("log info i = " + i); 18 //throw new RuntimeException(); 19 //continue; 20 } 21 } 22 } 23 24 public static void main(String[] args) { 25 final SyncException se = new SyncException(); 26 Thread t1 = new Thread(new Runnable() { 27 28 @Override 29 public void run() { 30 se.operation(); 31 } 32 },"t1"); 33 t1.start(); 34 } 35 }
4. 说明
对于web应用程序,异常释放锁的情况,如果不及时处理,很可能对你的应用程序业务逻辑产生严重的错误,比如你现在执行一个队列任务,很多对象都去在等待第一个对象正确执行完毕再去释放锁,但是第一个对象由于异常的出现,导致业务逻辑没有正常执行完毕,就释放了锁,那么可想而知后续的对象执行的都是错误的逻辑。所以,这一点一定要引起注意,在编写代码的时候一定要考虑周全。
二、synchronized代码块
1. 介绍
使用synchronized声明的方法在某些情况下是有弊端的,比如A线程调用同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的时间才能执行,这样的情况下可以使用synchronized代码块去优化代码执行时间,也就是通常所说的减小锁粒度。
示例:【com.xxy.base.sync006 Optimize】
2. synchronized可以使用任意的Object进行加锁,用法比较灵活。
示例:【com.xxy.base.sync006 ObjectLock】
3.另外特别注意一个问题,就是不要使用String的常量加锁,会出现死循环问题
示例:【com.xxy.base.sync006 StringLock】
4.锁对象的改变问题,当使用一个对象进行加锁的时候,要注意对象本身发生改变的时候,那么持有的锁就不同。如果对象本身不发生改变,那么依然是同步的,即使是对象的属性发生了改变。
示例:【com.xxy.base.sync006 ChangeLock ModifyLock】
5.死锁问题
示例:【com.xxy.base.sync006 DeadLock】