1 多线程安全问题
1.1 a++问题
见上篇
1.2 活跃性问题-死锁
- 死锁案例
public class MultiThreadError implements Runnable {
int flag = 1;
static Object o1 = new Object();
static Object o2 = new Object();
@Override
public void run() {
System.out.println("falg= " + flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("1");
}
}
}
if (flag == 0) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
MultiThreadError r1 = new MultiThreadError();
MultiThreadError r2 = new MultiThreadError();
r1.flag = 1;
r2.flag = 0;
new Thread(r1).start();
new Thread(r2).start();
}
}
1.3 对象发布和初始化的时候的安全问题
1.3.1 案例-构造函数没有初始化完毕
/**
* @Description 初始化没有完毕,就this赋值
* @Author tzb
* @Date 2021/3/8 21:38
* @Version 1.0
**/
public class MultiThreadError4 {
static Point point;
public static void main(String[] args) throws InterruptedException {
new PointMaker().start();
Thread.sleep(10);
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();
}
}
}
- 修改
1.3.2 案例-注册监听事件
package background;
import javax.xml.transform.Source;
import java.util.EventListener;
import java.util.function.Predicate;
/**
* @Description 观察者模式
* @Author tzb
* @Date 2021/3/8 21:53
* @Version 1.0
**/
public class MultiThreadError5 {
int count;
public MultiThreadError5(MySource source) {
source.registerListener(new EventListener() {
@Override
public void onEvent(Event e) {
System.out.println("我得到的数字是:" + count);
}
});
for (int i = 0; i < 10000; i++) {
System.out.print(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 {
}
}
注册了监听器后,在 onEvent
方法中就已经隐含地暴露了外部类的对象,EvenListener是匿名内部类,它持有外部类的引用,可以对count进行操作。如果构造函数对count赋值没有完成,即便是注册监听器,也有安全隐患。
1.3.3 案例-构造函数新建线程
错误做法
- 在构造函数拿到线程池的引用或者数据库的连接池,而创建连接池的时候都是在后台新开线程,
2 如何解决逸出
-
返回副本,针对返回private对象的情况
-
工厂模式,针对未完成初始化就把对象提供给外界
/**
* @Description 工厂模式修复初始化问题
* @Author tzb
* @Date 2021/3/9 22:42
* @Version 1.0
**/
public class MultiThreadError7 {
int count;
private EventListener listener;
private MultiThreadError7(MySource source) {
listener = new EventListener() {
@Override
public void onEvent(Event e) {
System.out.println("我得到的数字是:" + count);
}
};
for (int i = 0; i < 10000; i++) {
System.out.print(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