多线程核心8-3:线程三大安全问题之发布与逸出
其他
2021-01-30 21:26:59
阅读次数: 0
发布与逸出的概念
- 发布:对象能够在作用域范围外被使用,则这个对象被发布出去了
- 逸出:被发布到了不该发布的地方,以下是逸出的两种情况:
- 方法返回一个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(){
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(){
return states;
}
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;
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