一、线程安全的理解
一个多线程任务中存在多种结果(而我们想要的是唯一的结果)
二、多线程的特性
- 原子性
- 有序性
- 可见性
为了保证JMM可见性问题,引入了内存屏障,会严格规范指令重排序问题(排序有:编译排序、指令排序、内存排序)
三、线程安全的本质原因
- 共享变量的可见性问题:在java内存模型中,存在多个线程可以共享一个变量,当一个线程对该变量进行修改的时候没有被另一个操作该变量的线程读取到就产生的线程安全问题
- 重排序问题:一段代码到最终的指令的执行会经历3个阶段分别是编译重排序、指令重排序、内存重排序
四、常见的线程不安全类和安全类的实现
1、ArrayList===》Vector
2、HashMap===》ConcurrentHashMap
3、SimpleDateFormate
4、StringBuilder=》StringBuffer
五、线程不安全示例
package com.gpdi.security;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* @description:利用CountDownLatch创建6个线程,每个线程循环100次,并返回对应的结果
* @author: whs
* @date:2019
*/
public class CountDownLatchDemo2 {
public static void main(String[] args) {
//线程安全
// List myList = new Vector();
//线程不安全
List myList = new ArrayList();
//创建一个线程倒数计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < countDownLatch.getCount(); i++) {
Thread thread = new Thread(new CountDownLatchThread(myList, countDownLatch));
thread.start();
}
try {
// 主线程等待所有子线程执行完成,再向下执行
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(myList.size());
}
/**
* @description: 一、创建一个内部类线程
* <p>
* 二、这里推荐使用Runnable,不推荐使用Thread(Runnable 可以通过构造器进行传参)
*/
public static class CountDownLatchThread implements Runnable {
private List list;
public CountDownLatch countDownLatch1;
public CountDownLatchThread(List list, CountDownLatch countDownLatch1) {
this.list = list;
this.countDownLatch1 = countDownLatch1;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
list.add(new Object());
}
//让countDownLatch结束
countDownLatch1.countDown();
}
}
}
五、常见的线程安全的手段
1、synchronized
用法:
1、修饰方法
public synchronized static void main(String[] args) {
}
2、修饰代码块
static WithDrowMoney object = new WithDrowMoney();
synchronized (object) {
count += 1;
}
2、volatile
用法:修饰变量 volatile boolean flag;
作用:保证共享变量的可见性(对于像i++这种复合操作是没有办法控制的,这种需要保证共享变量原子性)