并发,每个Java开发者对这个词并不陌生,无论是研究技术还是面试,多少都会接触一些,但是你究竟对并发了解多少呢?是嘴上泛泛而谈,从没实践过,还是对并发大有研究,在项目中使用游刃有余,今天我们聊聊并发
你对并发究竟了解多少?
进程&线程,并发&并行,创建线程,线程API,线程状态转换,JMM模型,synchronized,volatile,死锁&活锁,设计模式,共享模型,JUC,CAS,MESI,线程池这些词汇你都不陌生,那么究竟掌握了多少呢?
基础的概念想必八股都背的够多了,可以自行搜索 ,问些不那么容易的吧
线程API记得哪些?知道如何使用么?
JMM模型原理是什么? 如何保证可见性、有序性、一致性?
Java对象头存储了哪些东西?
Monitor原理?
synchronized关键字怎么起作用的?
锁膨胀、自旋优化、偏向锁、JDK版本影响了解?
死锁活锁有哪些场景?
double-checked 单例模式如何编写?
volatile关键字如何起作用的?
读写屏障有哪些?
MESI协议了解吗?
JUC包了解哪些?
CAS无锁并发技术原理?
SimpleDateFormatter为何有线程问题?
原子引用如何安全实现?
字段更新器AtomicReferenceFieldUpdater了解吗?
原子数组AtomicXArray如何实现线程安全?
原子累加器LongAddr为什么比AtomicInteger有更高的性能?
并发共享模型了解过哪些?
并发设计模式有哪些?
HashMap在jdk1.8和jdk1.7之间的区别?
String为什么是线程安全的,源码是否读过?
以上的技术并不是并发的全部,之后的文章会更新技术细节,欢迎大家一起讨论技术
线程状态
线程状态为何有人说是五种,有人说是六种? 五种的是操作系统层面,六种是JVM层面
以下是代码模拟JVM层面的状态
@Slf4j(topic = "test.state")
public class TestState {
public static void main(String[] args) {
//处于new的状态,因为没start()
Thread t1 = new Thread("t1"){
@Override
public void run() {
// log.debug("running...");
}
};
// 里面的while(true)让这个线程一直在运行
// 但这是java层面的runnable:1.有可能拿到时间片 2.也有可能没拿到时间片 3.进入IO阻塞,这在Java层面都是runnable
Thread t2 = new Thread("t2"){
@Override
public void run() { //runnable
while(true){
}
}
};
t2.start();
//下面主线程会休眠0.5S,所以t3线程会先于主线程执行打印running...
// 等到主线程执行打印语句的时候,t3进入terminated状态
Thread t3 = new Thread("t3"){
@Override
public void run() { // terminated
log.debug("running...");
}
};
t3.start();
// Sleep时间足够长,所以打印语句的时候一直在waiting状态,而且是timed_waiting
Thread t4 = new Thread("t4"){
@Override
public void run() {
synchronized (TestState.class){
try {
Thread.sleep(1000000); //timed_waiting
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t4.start();
// join()方法要等待其他线程执行结束,这里是等待t2执行,但是t2是个死循环,所以t5会一直等待,没有时限的等待,是waiting状态
Thread t5 = new Thread("t5"){
@Override
public void run() {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t5.start();
// t4先上锁,t6就拿不到锁,会陷入blocked状态
Thread t6 = new Thread("t6"){
@Override
public void run() {
//t6想获得锁,但是锁在t4未释放
synchronized (TestState.class){ // blocked
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t6.start();
//休眠的是主线程
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1 state {}", t1.getState());
log.debug("t2 state {}", t2.getState());
log.debug("t3 state {}", t3.getState());
log.debug("t4 state {}", t4.getState());
log.debug("t5 state {}", t5.getState());
log.debug("t6 state {}", t6.getState());
}
}
JMM模型
JMM 即 Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着 CPU 寄存器、缓存、硬件内存、 CPU 指令优化等。
JMM 体现在以下几个方面
- 原子性 - 保证指令不会受到线程上下文切换的影响
- 可见性 - 保证指令不会受 cpu 缓存的影响
- 有序性 - 保证指令不会受 cpu 指令并行优化的影响
为什么i++也会发生线程问题?
下面是JVM字节码指令,并不能保证其原子性
getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
iadd // 自增
putstatic i // 将修改后的值存入静态变量i