• 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
  • 相关阅读:
    数值分析实验之平方根法解线性方程组(MATLAB代码)
    Packet Tracer 下载方法
    注册 Netacad (思科)账户 超详细流程
    数值分析实验之非线性方程求根(Python 现)
    数值分析实验之非线性方程求根(MATLAB实现)
    数值分析实验之矩阵的LU分解及在解线性方程组中的应用(java 代码)
    数值分析实验之矩阵的LU分解及在解线性方程组中的应用(MATLAB 代码)
    数值分析实验之矩阵的LU分解及在解线性方程组中的应用(Python 代码)
    数值分析实验之数值积分法(MATLAB代码)
    在python3中安装mysql扩展,No module named 'ConfigParser'
  • 原文地址:https://www.cnblogs.com/Mao-admin/p/9988489.html
Copyright © 2020-2023  润新知