zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    zbb20181207 springboot @ConfigurationProperties使用
    zbb20181206 logback,lombok 默认日志logback配置解析
    Spring Boot (8) 全局异常处理
    Spring Boot (7) JdbcTemplate访问数据库
    Spring Boot (6) Spring Data JPA
    Spring Boot (4) 静态页面和Thymeleaf模板
    Spring Boot (3) 热部署devtools
    Spring Boot (2) Restful风格接口
    Spring Boot (1) 构建第一个Spring Boot工程
    idea使用maven搭建ssm框架实现登陆商品增删改查
  • 原文地址:https://www.cnblogs.com/Mao-admin/p/9988489.html
Copyright © 2011-2022 走看看