View on GitHub

Dmarco-v.github.io

Dmarco_v的技术博客,Java开发相关技术积累、LeetCode刷题笔记

一、并发编程基础

1.线程与进程的区别

2.为什么使用多线程?

3.线程状态及其转换

转换过程:

sleep和wait的区别

为什么不能直接调用run方法

4.上下文切换

5.死锁

6.创建线程的方法

二、synchronized关键字

1.什么是synchronized关键字

2.使用的三种方式

3.双重校验锁实现单例模式

public class Singleton{
    private volatile static Singleton uniqueInstance;
    
    private Singleton(){
    }
    
    public static Singleton getUniqueInstance(){
        //先判断对象是否已经实例化
        if(uniqueInstance==null){
            //给类对象加锁
            synchronized(Singleton.class){
                if(uniqueInstance==null){
                    uniqueInstance=new Singleton();
                }
            }
        }
    }
}

设计双重校验锁的原因:如果只有外层的if语句,没有内层,此时如果instance==null,可能会有两个线程同时进入内层,从而使得两个线程都会执行实例化操作;如果只有内层没有外层,对象已被实例化还要进行一次加锁,不合理。

此外,还需要注意用volatile关键字修饰uniqueInstance的必要性。uniqueInstance=new Singleton();这段代码会分三步执行:

由于JVM具有指令重排的特性,执行顺序可能会变成1、3、2,在多线程环境下可能会导致一个线程获得还没有初始化的实例,例如线程1执行了1,3而线程而调用getUniqueInstance方法发现其不为空,因此返回uniqueInstance,但此时的uniqueInstance还未被初始化。

使用volatile修饰可以禁止JVM的指令重排,保证多线程环境下也能正常运行。

4.底层原理

当修饰代码块时。

当修饰方法时。

5.JDK1.6之后的优化

锁主要有四种状态:

6.synchronized和ReentrantLock的区别

三、volatile关键字

1.Java内存模型

2.并发编程的三个重要特性

3.volitile和synchronized的区别

四、ThreadLocal

1.ThreadLocal原理

2.ThreadLocal内存泄漏问题

五、线程池

1.为什么使用线程池

2.Runnable和Callable的区别

3.创建线程池

4.线程池原理分析