学习
https://time.geekbang.org/column/article/83267
https://time.geekbang.org/column/article/83087
并发编程有三个核心的问题
1、分工
指的是如何高效地拆解任务并分配给线程,Java SDK 并发包里的 Executor、Fork/Join、Future 本质上都是一种分工方法。除此之外,并发编程领域还总结了一些设计模式,基本上都是和分工方法相关的,例如生产者 - 消费者、Thread-Per-Message、Worker Thread 模式等都是用来指导你如何分工的。
2、同步
分工之后就要考虑多个线程如何执行,期间就涉及到了同步
同步可以总结为:当某个条件不满足时,线程需要等待,当某个条件满足时,线程需要被唤醒执行
例如,在生产者 - 消费者模型里,也有类似的描述,“当队列满时,生产者线程等待,当队列不满时,生产者线程需要被唤醒执行;当队列空时,消费者线程等待,当队列不空时,消费者线程需要被唤醒执行。”
3、互斥
分工、同步主要强调的是性能,但是我们还要保证正确性,也就是线程安全,
保证同一时刻只允许一个线程访问共享资源
并发程序里,当多个线程同时访问同一个共享变量的时候,结果是不确定的。不确定,则意味着可能正确,也可能错误,事先是不知道的。M而导致不确定的主要源头是可见性问题、有序性问题和原子性问题,为了解决这三个问题引入了JMM
解决互斥基本使用的是加锁, synchronized、SDK 里的各种 Lock 都能解决互斥问题。虽说锁解决了安全性问题,但同时也带来了性能问题
但是也有其他的方案避免因为共享变量导致线程安全问题,比如Thread Local 和 final 关键字,还有一种 Copy-on-write 的模式。
扩展
- 什么场景下使用CountDownLatch
比如多线程解析excel的多个sheet,可以开启多个线程去解析,每个线程解析一个sheet,解析完成之后执行countDown方法,在最后执行await()方法,直到所有线程执行完成
参考自https://ifeve.com/talk-concurrency-countdownlatch/
- countDown代码演示 相当于一场考试。所有的学生交了试卷之后,老师才开始离开教师.
代码来自https://www.cnblogs.com/bqcoder/p/6089101.html
public static void testCountDownLatch(){
int threadCount = 10;
final CountDownLatch latch = new CountDownLatch(threadCount);
for(int i=0; i< threadCount; i++){
new Thread(()->{
System.out.println("线程" + Thread.currentThread().getId() + "开始出发");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getId() + "已到达终点");
latch.countDown();
}).start();
}
try {
//会等待所有的线程执行完毕
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("10个线程已经执行完毕!开始计算排名");
}
final 关键字和多线程的关系(happens-before原则)
public class FinalTest {
int i;//普通变量
final int j;
static FinalTest obj;
public FinalTest(){
i = 1;
j = 2;
}
public static void writer(){
obj = new FinalTest();
}
public static void reader(){
FinalTest tets = obj;//读对象引用
int a = tets.i;//a 和b 不一定等于1,2 因为会有构造函数溢出的可能。
int b = tets.j;
}
}