Java并发技术联想

94 阅读4分钟

并发,每个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层面

image.png

image.png

以下是代码模拟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