5.并发编程-synchronized 细节说明

 

并发编程-synchronized 细节说明

1. synchronized-锁重入 & 异常释放锁 说明

  • * 关键字synchronized 拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象的锁后,再次请求此对象时可以再次得到该对象的锁;

2. synchronized-代码块 说明

  • * 使用synchronized声明的方法在某些情况下是比较极端的(存在弊端):线程A调用同步的方法执行一段很长时间的任务,那么B线程就必须等待比较长的时间才能执行;

解决方法:使用synchronized代码块去优化代码执行的时间,也就是减少锁的粒度;

  • * 特别注意一个问题:就是不要使用String的常量进行加锁,会出现死循环的问题。
  • * 锁对象的改变问题:当使用一个对象进行加锁的时候,要注意对象本身发生变化的时候,那么持有的锁就不同。
  • 如果对象本身不发生变化,那么依然是同步的,即使对象的属性发生变化也是同步的。

 

实例:
SyncDubbo1.java 和 SyncDubbo2.java

/**
 * synchronized的重入
 * @@author Maozw
 *
 */
public class SyncDubbo1 {

  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) {
    final SyncDubbo1 sd = new SyncDubbo1();
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        sd.method1();
      }
    });
    t1.start();
  }
}
/**
 * synchronized的重入
 * @@author Maozw
 *
 */
public class SyncDubbo2 {

  static class Main {
    public int i = 10;
    public synchronized void operationSup(){
      try {
        i--;
        System.out.println("Main print i = " + i);
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  static class Sub extends Main {
    public synchronized void operationSub(){
      try {
        while(i > 0) {
          i--;
          System.out.println("Sub print i = " + i);
          Thread.sleep(100);        
          this.operationSup();
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) {

    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        Sub sub = new Sub();
        sub.operationSub();
      }
    });

    t1.start();
  }
}

 

 出现异常会释放锁

示例:SyncException.java
说明:对于web程序,异常释放锁的情况,如果来不及及时处理,很可能对应用程序的业务逻辑产出错误:如执行一个队列任务,很多对象都去等待第一个对象执行完成并释放锁,但是第一个对象由于异常原因,导致业务逻辑没有正常执行完成,就释放了锁,那么后续任务就会产生一些问题;所以这个问题需要注意;

/**
 * synchronized异常
 * @@author Maozw
 *
 */
public class SyncException {

  private int i = 0;
  public synchronized void operation(){
    while(true){
      try {
        i++;
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + " , i = " + i);
        if(i == 20){
          //Integer.parseInt("a");
          throw new RuntimeException();
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) {

    final SyncException se = new SyncException();
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        se.operation();
      }
    },"t1");
    t1.start();
  }
}

 

synchronized-代码块 说明

使用synchronized声明的方法在某些情况下是比较极端的(存在弊端):线程A调用同步的方法执行一段很长时间的任务,那么B线程就必须等待比较长的时间才能执行;
* 解决方法:使用synchronized代码块去优化代码执行的时间,也就是减少锁的粒度;
* 示例:ObjectLock.java
* 说明:synchronized可以使用任务的Object对象进行加锁,用法比较灵活;

 

 1 /**
 2  * 使用synchronized代码块加锁,比较灵活
 3  * @@author Maozw
 4  *
 5  */
 6 public class ObjectLock {
 7 
 8   public void method1(){
 9     synchronized (this) {    //对象锁
10       try {
11         System.out.println("do method1..");
12         Thread.sleep(2000);
13       } catch (InterruptedException e) {
14         e.printStackTrace();
15       }
16     }
17   }
18 
19   public void method2(){        //类锁
20     synchronized (ObjectLock.class) {
21       try {
22         System.out.println("do method2..");
23         Thread.sleep(2000);
24       } catch (InterruptedException e) {
25         e.printStackTrace();
26       }
27     }
28   }
29 
30   private Object lock = new Object();
31   public void method3(){        //任何对象锁
32     synchronized (lock) {
33       try {
34         System.out.println("do method3..");
35         Thread.sleep(2000);
36       } catch (InterruptedException e) {
37         e.printStackTrace();
38       }
39     }
40   }
41 
42 
43   public static void main(String[] args) {
44 
45     final ObjectLock objLock = new ObjectLock();
46     Thread t1 = new Thread(new Runnable() {
47       @Override
48       public void run() {
49         objLock.method1();
50       }
51     });
52     Thread t2 = new Thread(new Runnable() {
53       @Override
54       public void run() {
55         objLock.method2();
56       }
57     });
58     Thread t3 = new Thread(new Runnable() {
59       @Override
60       public void run() {
61         objLock.method3();
62       }
63     });
64 
65     t1.start();
66     t2.start();
67     t3.start();
68   }
69 }
View Code

 

 特别注意一个问题:就是不要使用String的常量进行加锁,会出现死循环的问题。

* 示例:StringLock.java
* 说明:

/**
 * synchronized代码块对字符串的锁,注意String常量池的缓存功能
 * @@author Maozw
 *
 */
public class StringLock {

  public void method() {
    //new String("字符串常量")
    synchronized ("字符串常量") {
      try {
        while(true){
          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();
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        stringLock.method();
      }
    },"t1");
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        stringLock.method();
      }
    },"t2");

    t1.start();
    t2.start();
  }
}

 

锁对象的改变问题:

* 说明:

  • 1. 当使用一个对象进行加锁的时候,要注意对象本身发生变化的时候,那么持有的锁就不同。
  • 2. 如果对象本身不发生变化,那么依然是同步的,即使对象的属性发生变化也是同步的。

* 示例:ModifyLock.java

  /**
   * 同一对象属性的修改不会影响锁的情况
   * @@author Maozw
   *
   */
  public class ModifyLock {

    private String name ;
    private int age ;

    public String getName() {
      return name;
    }
    public void setName(String name) {
      this.name = name;
    }
    public int getAge() {
      return age;
    }
    public void setAge(int age) {
      this.age = age;
    }

    public synchronized void changeAttributte(String name, int age) {
      try {
        System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 开始");
        this.setName(name);
        this.setAge(age);

        System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 修改对象内容为: "
            + this.getName() + ", " + this.getAge());

        Thread.sleep(2000);
        System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 结束");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }

    public static void main(String[] args) {
      final ModifyLock modifyLock = new ModifyLock();
      Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
          modifyLock.changeAttributte("张三", 20);
        }
      },"t1");
      Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
          modifyLock.changeAttributte("李四", 21);
        }
      },"t2");

      t1.start();
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      t2.start();
    }
  }
View Code

 

死锁问题 :

* 说明: 在设计程序时就应该避免双方相互持有对方的锁的情况
* 示例:

/**
   * 死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况
   * @@author Maozw
   *
   */
  public class DeadLock implements Runnable{

    private String tag;
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public void setTag(String tag){
      this.tag = tag;
    }

    @Override
    public void run() {
      if(tag.equals("a")){
        synchronized (lock1) {
          try {
            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");
            Thread.sleep(2000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          synchronized (lock2) {
            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");
          }
        }
      }
      if(tag.equals("b")){
        synchronized (lock2) {
          try {
            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");
            Thread.sleep(2000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          synchronized (lock1) {
            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");
          }
        }
      }
    }

    public static void main(String[] args) {

      DeadLock d1 = new DeadLock();
      d1.setTag("a");
      DeadLock d2 = new DeadLock();
      d2.setTag("b");

      Thread t1 = new Thread(d1, "t1");
      Thread t2 = new Thread(d2, "t2");

      t1.start();
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      t2.start();
    }
  }
View Code

 

posted @ 2018-11-20 13:38  花神47699  阅读(155)  评论(0编辑  收藏  举报