1.1 什么是线程安全
1.2 线程不安全:get同时set
1.3 什么情况下会出现线程安全问题,怎么避免?
1.3.1 运行结果错误:a++多线程下出现消失的请求现象
/**
* MultiThreadsError
*
* @author venlenter
* @Description: 普通a++会导致count叠加错误,以下程序已优化处理
* @since unknown, 2020-05-07
*/
public class MultiThreadsError3 implements Runnable {
int index = 0;
final boolean[] marked = new boolean[10000000];
static AtomicInteger realIndex = new AtomicInteger();
static AtomicInteger wrongCount = new AtomicInteger();
static MultiThreadsError3 instance = new MultiThreadsError3();
static volatile CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
static volatile CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);
@Override
public void run() {
marked[0] = true;
for (int i = 0; i < 10000; i++) {
try {
cyclicBarrier2.reset();
cyclicBarrier1.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
index++;
try {
cyclicBarrier1.reset();
cyclicBarrier2.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
realIndex.incrementAndGet();
//在原基础上加synchronized
synchronized (instance) {
if (marked[index] && marked[index - 1]) {
System.out.println("发生错误:" + index);
wrongCount.incrementAndGet();
}
marked[index] = true;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("表面上结果是:" + instance.index);
System.out.println("真正运行的次数:" + realIndex.get());
System.out.println("错误次数:" + wrongCount.get());
}
}
//输出结果
表面上结果是:20000
真正运行的次数:20000
错误次数:0
1.3.2 活跃性问题:死锁、活锁、饥饿
/**
* MultiThreadError
*
* @author venlenter
* @Description: 第二章线程安全问题,演示死锁
* @since unknown, 2020-05-11
*/
public class MultiThreadError implements Runnable {
int flag = 1;
static Object o1 = new Object();
static Object o2 = new Object();
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();
}
@Override
public void run() {
System.out.println("flag = " + 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");
}
}
}
}
}
//输出结果
flag = 1
flag = 0
//程序一直等待不结束
1.3.3 对象发布和初始化时的安全问题
什么是发布?
什么是溢出?
(1)在构造函数中未初始化完毕就把this赋值出去了
/**
* MultiThreadsError4
*
* @author venlenter
* @Description: 初始化未完毕,就this赋值
* @since unknown, 2020-05-12
*/
public class MultiThreadsError4 {
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;
Point(int x, int y) throws InterruptedException {
this.x = x;
//这里先行给point赋值this,此时外部拿到point对象只有x,没有y的值
MultiThreadsError4.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();
}
}
}
//输出结果
//可能
Point{x=1, y=1}
//也可能
Point{x=1, y=0}
(2)隐式逸出————注册监听事件
/**
* MultiThreadsError5
*
* @author venlenter
* @Description: 观察者模式
* @since unknown, 2020-05-12
*/
public class MultiThreadsError5 {
int count;
public MultiThreadsError5(MySource source) {
source.registerListener(new EventListener() {
@Override
//这里EventListener是一个匿名内部类,实际上也用了count这个外部引用变量,当count未初始化完成,拿到的值就还是0
public void onEvent(Event e) {
System.out.println("\n我得到的数字是:" + 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();
MultiThreadsError5 multiThreadsError5 = new MultiThreadsError5(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 {
}
}
//输出结果
012345678910...
我得到的数字是:0
...
(3)构造函数中运行线程
/**
* MultiThreadsError6
*
* @author venlenter
* @Description: 构造函数中新建线程
* @since unknown, 2020-05-14
*/
public class MultiThreadsError6 {
private Map states;
public MultiThreadsError6() {
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 getStates() {
return states;
}
public static void main(String[] args) {
MultiThreadsError6 multiThreadsError6 = new MultiThreadsError6();
//在构造函数中states还未初始化完成,就get
System.out.println(multiThreadsError6.getStates().get("1"));
}
}
//输出结果
Exception in thread "main" java.lang.NullPointerException
at ConcurrenceFolder.mooc.threadConcurrencyCore.background.MultiThreadsError6.main(MultiThreadsError6.java:34)
1.4 如何解决逸出
3.1 性能问题有哪些体现、什么是性能问题
3.2 为什么多线程会带来性能问题
(1)调度:上下文切换
(2)协作:内存同步
(1)你知道有哪些线程不安全的情况
(2)平时哪些情况下需要额外注意线程安全问题?
(3)什么是多线程的上下文切换?
笔记来源:慕课网悟空老师视频《Java并发核心知识体系精讲》