Skip to content
1.什么是进程、线程、协程?
  • 进程:本质上是一个独立执行的程序,例如QQ、微信等
  • 线程:操作系统进行运算调度的最小单位,一个进程中可以并发多个线程,每条线程执行不同的任务
  • 协程:轻量级的线程,也称之为用户级别的线程就叫协程,一个线程可以多个协程
2.并发和并行的区别?
  • 并发:指的是在同一时间段内,多个任务交替执行
  • 并行:指的是在同一时间点上,多个任务同时进行执行
  • 并发本质上是一个CPU快速切换处理多个任务,并行是多个CPU同时处理多个任务
3.创建线程有几种方式?
  1. 继承Thread
  2. 实现Runnable接口
  3. 使用Callable和FutureTask
  4. 使用线程池
  5. Spring的定时任务等…… 注:哪种方法最常用?2和4方法常用。详见创建线程的几种方式
4.线程的常见方法sleep/yield/join,wait/notify/notifyAll, 分别解释下
  • 属于线程Thread的方法
    • sleep:让线程暂缓执行,等待预计时间之后再恢复;交出CPU使用权但不会释放锁,线程进入阻塞状态,结束后进入就绪状态。
    • yield:暂停当前线程的对象执行权,去执行其他线程,交出CPU使用权,不会释放锁;但是它不会进入阻塞状态,而是一直处于就绪状态。
    • join:让调用join方法的线程先执行完毕,然后在执行其他线程
  • 属于Object的方法
    • wait:让线程进入等待状态,直到notifynotifyAll通知才会唤醒,某线程调用此方法会释放锁;
    • notify:随机从等待队列中通知一个持有相同锁的线程
    • notifyAll:唤醒全部线程
  • 注意:sleep不会释放锁,而wait会释放锁
5.你能画出多线程状态是如何流转的吗?

多线程状态流转图

6.你能举例说明几个业务代码使用多线程的例子吗?
  1. 发送短信使用多线程
  2. 调用第三方接口,比如消息推送,推送到个手机厂商
  3. 将日志持久化使用多线程
  4. 上面3个主要是异步任务,还有一些同步任务,例如定时备份数据库
  5. 分布式计算,主要是大数据的离线计算
  6. Netty网络编程通过多线程发布消息队列消息
7.你能想到哪些数据结构不是线程安全的?

ArrayList、HashMap、LinkedList

8.那么哪些是线程安全的?

首先可以加锁,然后jdk内置了许多线程安全的数据结构。

  1. ConcurrentHashMap
  2. ConcurrentLinkedDeque
  3. ConcurrentLinkedQueue
  4. ConcurrentSkipListMap
  5. ConcurrentSkipSet
  6. CopyOnWriteArrayList
  7. CopyOnWriteArraySet
9.聊聊volatile关键字,它和synchronized有什么区别?
  • volatile关键字保证了共享变量的可见性,使得其他线程能立即看到其值的变化,避免脏读;相当于轻量级的synchronized
  • volatile:保证可见性,但是不能保证原子性
  • synchronized:保证可见性,也保证原子性
10.使用volatile关键字有什么特点或注意事项?
  1. 不能修饰写入操作依赖当前值的变量,比如num++、num=num+1,因为其本质不是原子操作
  2. 由于禁止指令重排,所以JVM优化将不对其生效,效率上有所削减
11.为什么会出现脏读?
  • JMM规定所有的变量存在在主内存,每个线程有自己的工作内存
  • 线程对变量的操作都在工作内存中进行,不能直接对主内存就行操作
  • 使用volatile修饰变量,每次读取前必须从主内存属性最新的值,每次写入需要立刻写到主内存中

为什么jvm会出现脏读

12.volatile可以避免指令重排,那么什么是指令重排?

不同线程之间的执行顺序可能会被改变,对于单线程来说这个改变是不会影响结果的,但是多线程共同拥有的全局变量,可能会在指令重排时被优化,导致执行顺序改变。

java
int a = 3 //1
int b = 4 //2
int c =5 //3 
int h = a*b*c //4

比如如上四行代码,1234和2314的执行结果是相同的,所以jvm在某些条件下的优化会修改代码的执行顺序,对于单线程来说是不影响的。

13.并发编程的三要素是什么?
  1. 原子性:操作要么全部完成,要么不完成,没有中间状态;不存在上下文切换;
    • 原子操作:int n = 1
    • 非原子操作:n++
  2. 可见性:一个线程对共享变量的修改能够被其他线程立即感知到。
  3. 有序性:程序执行的结果按照一定的顺序来保证。
14.如何把多步操作变成原子操作?
  1. 获取锁对象,操作结束后释放锁
  2. 将操作封装到方法中,对方法使用synchronized
java
public class XdTest {
    private int num = 0;
    
    //使用lock,每个对象都是有锁,只有获得这个锁才可以进行对应的操作
    Lock lock = new ReentrantLock();
    public  void add1(){
        lock.lock();
        try {
            num++;
        }finally {
            lock.unlock();
        }
    }
    
    //使用synchronized,和上述是一个操作,这个是保证方法被锁住而已,上述的是代码块被锁住
    public synchronized void add2(){
        num++;
    }
}
15.你对CAS知道多少,直到存在的ABA问题吗?

CAS是一种乐观锁先比较再替换,CAS修改时会先比较内存位置的值与预期值是否相等,如果不相等修改失败交给线程做其他处理。

ABA问题:修改后的值和CAS预期值相同,也就是该内存虽然被修改过,但修改的值相同;解决办法是添加一个版本号字段,不止比较值还比较版本号。

16.能不能谈谈你对线程安全的理解?

方法在并发环境被调用时能够正确处理多个线程之间的共享变量,使程序功能正确完成。

17.synchronized是如何实现的?
  1. 每个Java对象都有一个内置的锁(也称为监视器锁或互斥锁)。
  2. 当线程进入一个被synchronized修饰的方法或代码块时,它会尝试获取该对象的锁。
  3. 如果锁没有被其他线程占用,则当前线程获取到锁,可以执行被保护的代码。
  4. 如果锁已被其他线程占用,当前线程将被阻塞,直到锁被释放。
  5. 当线程执行完被保护的代码后,会释放锁,让其他线程可以获取锁并执行。
18.如何理解AQS?
  • 在AQS内部,通过维护一个FIFO队列和一个volatile的int类型的state变量,实现了对象锁的占有和释放。
  • 当state=1时表示锁已被占有,通过CAS操作修改state变量的值。
  • 对于加锁失败的线程,会被封装成一个Node节点并排队在队列尾部。
19.什么是Unsafe?

它可以直接操作内存或执行一些特殊操作的方法,它执行硬件级别操作绕过了Java语言的安全检查机制;它并不希望被一般的开发者调用,不可以直接new只能通过反射获取对象。

20.synchronized和reentrantLock区别?
  1. 二者都是可重入锁
  2. synchronized是内置的,而reentrantLock通过java代码实现
  3. reentrantLoc只能手动释放锁
  4. synchronized只能是非公平锁,ReentrantLock可以实现公平锁和非公平锁