发布与逸出的概念
- 发布:对象能够在作用域范围外被使用,则这个对象被发布出去了
- 逸出:被发布到了不该发布的地方,以下是逸出的两种情况:
- 方法返回一个private对象
- 未完成初始化(构造函数还未执行完毕)就将对象提供给外界
- 在构造函数中未完成初始化完毕就用this赋值
- 隐式逸出–注册监听事件
- 构造函数中运行线程
例子与解决方法
方法返回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")); } }