多线程核心8-3:线程三大安全问题之发布与逸出

发布与逸出的概念

  • 发布:对象能够在作用域范围外被使用,则这个对象被发布出去了
  • 逸出:被发布到了不该发布的地方,以下是逸出的两种情况:
    • 方法返回一个private对象
    • 未完成初始化(构造函数还未执行完毕)就将对象提供给外界
      1. 在构造函数中未完成初始化完毕就用this赋值
      2. 隐式逸出–注册监听事件
      3. 构造函数中运行线程

例子与解决方法

方法返回private对象的情况

  • 在例子中return了一个private修饰的对象,即将该私有对象发布出去了
      public class MultiThreadError3 {
          
          
          private Map<String, String> states;
      
          public MultiThreadError3(){
          
          
              states = new HashMap<>();
              states.put("1", "周一");
              states.put("2", "周二");
              states.put("3", "周三");
              states.put("4", "周四");
          }
      
          public Map<String, String> getStates(){
          
          
              //将private修饰的变量发布出去了
              return states;
          }
      
          public static void main(String[] args) {
          
          
              MultiThreadError3 multiThreadError3 = new MultiThreadError3();
              Map<String, String> states = multiThreadError3.getStates();
      
              System.out.println(states.get("1"));
              states.remove("1");
              System.out.println(states.get("1"));
          }
      }
      
      ```
    
  • 外界可以直接调用该私有对象,并且对它进行修改,显然这样做是危险的
    在这里插入图片描述
  • 解决方法:使用“副本”,不让其他程序修改私有对象
    public class MultiThreadError3 {
          
          
      private Map<String, String> states;
    
      public MultiThreadError3(){
          
          
          states = new HashMap<>();
          states.put("1", "周一");
          states.put("2", "周二");
          states.put("3", "周三");
          states.put("4", "周四");
      }
    
      public Map<String, String> getStates(){
          
          
          //将private修饰的变量发布出去了
          return states;
      }
    
      /**
       * 获取副本的方法
       * @return
       */
      public Map<String, String> getStatesImproved(){
          
          
          return new HashMap<>(states);
      }
    
      public static void main(String[] args) {
          
          
          MultiThreadError3 multiThreadError3 = new MultiThreadError3();
          Map<String, String> states = multiThreadError3.getStates();
    
          System.out.println(multiThreadError3.getStatesImproved().get("1"));
          multiThreadError3.getStatesImproved().remove("1");
          System.out.println(multiThreadError3.getStatesImproved().get("1"));
      }
    }
    
    

构造函数还未执行完毕情况

1、在构造函数中未完成初始化完毕就用this赋值

  • 未完成初始化就用赋值,因为x和y的赋值时序有先后,不能保证每次拿到的值都是一样的
      public class MultiThreadError4 {
          
          
          static Point point;
      
          public static void main(String[] args) throws InterruptedException {
          
          
              new PointMaker().start();
              Thread.sleep(105);
              if (point != null){
          
          
                  System.out.println(point);
              }
          }
      }
      
      class Point{
          
          
          private final int x, y;
      
          public Point(int x, int y) throws InterruptedException {
          
          
              this.x = x;
      
              MultiThreadError4.point = this;
              Thread.sleep(100);
      
              this.y = y;
          }
      
          @Override
          public String toString() {
          
          
              return "Point{" +
                      "x=" + x +
                      ", y=" + y +
                      '}';
          }
      }
      
      class PointMaker extends Thread{
          
          
          @Override
          public void run() {
          
          
              try {
          
          
                  new Point(1, 1);
              } catch (InterruptedException e) {
          
          
                  e.printStackTrace();
              }
          }
      }
      ```
    

2、隐式逸出–注册监听事件

  • 在注册监听器时,匿名内部类持有了外部类的引用count,发生线程逸出
      public class MultiThreadError5 {
          
          
          int count;
      
          /**
           * 在匿名内部类中持有外部类的引用count,发生线程逸出
           * @param source
           */
          public MultiThreadError5(MySource source){
          
          
              source.registerListener(new EventListener() {
          
          
                  @Override
                  public void onEvent(Event e) {
          
          
                      System.out.println("\n我得到的数字是:" + count);
                  }
              });
              for (int i = 0; i < 10000; i++) {
          
          
                  System.out.println(i);
              }
              count = 100;
          }
      
          public static void main(String[] args) {
          
          
              MySource mySource = new MySource();
              new Thread(new Runnable() {
          
          
                  @Override
                  public void run() {
          
          
                      try {
          
          
                          Thread.sleep(10);
                      } catch (InterruptedException e) {
          
          
                          e.printStackTrace();
                      }
                      mySource.eventCome(new Event() {
          
          
                      });
                  }
              }).start();
              MultiThreadError5 multiThreadError5 = new MultiThreadError5(mySource);
          }
      
          static class MySource {
          
          
      
              private EventListener listener;
      
              void registerListener(EventListener eventListener) {
          
          
                  this.listener = eventListener;
              }
      
              void eventCome(Event e) {
          
          
                  if (listener != null) {
          
          
                      listener.onEvent(e);
                  } else {
          
          
                      System.out.println("还未初始化完毕");
                  }
              }
      
          }
      
          interface EventListener {
          
          
      
              void onEvent(Event e);
          }
      
          interface Event {
          
          
      
          }
      }
      ```
    
  • 使用工厂模式来改造,即不让外界直接操作构造方法,将其改为private,然后将所有准备工作做完再让外部来调用
      public class MultiThreadError7 {
          
          
          int count;
          private EventListener listener;
      
          private MultiThreadError7(MySource source){
          
          
              source.registerListener(new EventListener() {
          
          
                  @Override
                  public void onEvent(Event e) {
          
          
                      System.out.println("\n我得到的数字是:" + count);
                  }
              });
              for (int i = 0; i < 10000; i++) {
          
          
                  System.out.println(i);
              }
              count = 100;
          }
      
          public static MultiThreadError7 getInstance(MySource source) {
          
          
              MultiThreadError7 safeListener = new MultiThreadError7(source);
              source.registerListener(safeListener.listener);
              return safeListener;
          }
      
          public static void main(String[] args) {
          
          
              MySource mySource = new MySource();
              new Thread(new Runnable() {
          
          
                  @Override
                  public void run() {
          
          
                      try {
          
          
                          Thread.sleep(10);
                      } catch (InterruptedException e) {
          
          
                          e.printStackTrace();
                      }
                      mySource.eventCome(new Event() {
          
          
                      });
                  }
              }).start();
              MultiThreadError7 multiThreadError5 = new MultiThreadError7(mySource);
          }
      
          static class MySource {
          
          
      
              private EventListener listener;
      
              void registerListener(EventListener eventListener) {
          
          
                  this.listener = eventListener;
              }
      
              void eventCome(Event e) {
          
          
                  if (listener != null) {
          
          
                      listener.onEvent(e);
                  } else {
          
          
                      System.out.println("还未初始化完毕");
                  }
              }
      
          }
      
          interface EventListener {
          
          
      
              void onEvent(Event e);
          }
      
          interface Event {
          
          
      
          }
      }
      
      ```
    

3、构造函数中运行线程

  • 会出现空指针异常,因为线程一旦start,就认为对象已经初始化完成,不会再去管其中的子线程中对象是否初始化完成
      public class MultiThreadError6 {
          
          
          private Map<String, String> states;
      
          public MultiThreadError6() {
          
          
              new Thread(new Runnable() {
          
          
                  @Override
                  public void run() {
          
          
                      states = new HashMap<>();
                      states.put("1", "周一");
                      states.put("2", "周二");
                      states.put("3", "周三");
                      states.put("4", "周四");
                  }
              }).start();
          }
      
          public Map<String, String> getStates() {
          
          
              return states;
          }
      
          public static void main(String[] args) throws InterruptedException {
          
          
              MultiThreadError6 multiThreadsError6 = new MultiThreadError6();
              System.out.println(multiThreadsError6.getStates().get("1"));
          }
      }
    

猜你喜欢

转载自blog.csdn.net/weixin_44863537/article/details/112594145