我是 javapub,一名 Markdown 程序员从‍,八股文种子选手。

面试官: 说说你对多线程的了解?

提名人: 多线程便是同时运行多个线程,完成一件事的并行处理。比方开个程序,同时下载多个文件,同时处理多个客户端请求等等。 面试官:那什么是线程安全的?举个比方?

提名人: 线程安全便是多个线程拜访同一个目标或调用同一办法时,目标或办法内部的状况能保证正确。举个比方,String 是线程安全的,由于 String 内部的 char 数组是final的,不可变的。

public final class String {
    private final char value[];
}

面试官:ArrayList线程安全吗?

提名人: ArrayList 不是线程安全的,由于:

transient Object[] elementData; // elementData 能够改变 

如果多个线程同时拜访一个 ArrayList,其间一个线程正在扩容数组,这时另一个线程在读或添加元素,很或许引起空指针或者越界异常。 面试官:HashMap 呢?线程安全吗?

提名人: HashMap 也不是线程安全的,跟 ArrayList 一样,HashMap 在多线程下也或许发生死循环、数据丢掉等问题。由于:

transient Node<K,V>[] table;

并发情况下,比方两个线程同时 put 新键值对,都从头扩容了数组,都做旧数组到新数组的迁移工作,这就会发生数据丢掉的问题。 面试官:那怎么处理 HashMap 的线程安全问题?

提名人: 有几种常见的处理HashMap线程不安全的办法:

  1. Collections.synchronizedMap():返回一个线程安全的 HashMap,内部运用锁机制同步拜访 HashMap。
  2. ConcurrentHashMap:Java 7 发布的线程安全的 HashMap。内部运用锁分段技能完成线程安全,并发度很高。
  3. Hashtable:Hashtable 是留传的线程安全 HashMap,内部也运用同一把锁,并发度低。不推荐运用。
  4. 外包装:能够运用 Lock 或者 synchronized 关键字对 HashMap 进行外包装,完成线程安全。

面试官:谢谢,内容很详细!总结一下,线程安全对咱们来说很重要,在编写代码时要时刻考虑清楚。

提名人: 是的,总结如下:

  1. 多线程环境下,需谨慎运用非线程安全的类。如 ArrayList、HashMap。
  2. 尽量挑选Java供给的线程安全类,如String、ConcurrentHashMap。
  3. 如果有必要运用非线程安全的类,需要对其进行额外的同步措施,如加锁或者运用同步包装类。
  4. 在编写代码时,要时刻考虑目标或办法的状况是否在多线程下也能保持一致和正确。这便是咱们说到的“线程安全”。 多谢面试官的提问,让我对Java多线程和线程安全有一个比较全面和体系的温习,这些内容对我今后的学习和工作会很有协助。

面试官: 说说 wait() notify() notifyAll() 的差异?

提名人: 这三个办法都是用来协调线程间通讯的。差异如下:

  • wait():让当时线程等候,直到其他线程调用 notify() 办法告诉,或经过指定的时刻后从头等候。
  • notify():唤醒等候在此目标上的一个线程。如果有多个线程等候,则唤醒优先级最高的线程。
  • notifyAll():唤醒等候在此目标上的一切线程。

面试官:举个比方解说下?

提名人: 这里是一个经典的出产者顾客模型来解说这三个办法:

public class ProducerConsumer {
    private int product = 0;
    private boolean isProduced = false;
    public synchronized void produce() {
        // 等候,直到产品被消费
        while (isProduced) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 出产产品
        product++;
        isProduced = true;
        // 告诉顾客消费产品
        notifyAll();
    }
    public synchronized void consume() {
        // 等候,直到有产品出产
        while (!isProduced) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }     
        }
        // 消费产品
        product--;
        isProduced = false;
        // 告诉出产者出产产品
        notifyAll();
    }
}

这里 wait() 办法使出产线程和消费线程在产品未安排妥当时等候,notifyAll() 办法在产品安排妥当时唤醒等候线程。

面试官:sleep() 办法和 wait() 办法有什么差异?

提名人: sleep() 和 wait() 的主要差异在于:

  1. wait() 办法开释锁,sleep() 办法不开释锁。
  2. wait() 办法通常被用于线程间通讯,sleep() 办法用于暂停线程指定时刻。
  3. wait() 办法能够在没有指定时刻的情况下一向等候,sleep() 办法有必要指定等候时刻。
  4. wait() 办法被唤醒后有必要从头获取锁,sleep() 办法睡醒后直接继续履行。 所以简略来说:wait() 能够用于线程间的同步,sleep() 主要用于暂停线程指定时刻。

面试官:完美!谢谢你,这些知识点解说的很透彻。

提名人: 不客气,多谢面试官的提问,让我对 wait() notify() notifyAll() 以及它们与 sleep() 的差异有了更深的了解,这些都是非常重要的多线程知识点,我会继续加深了解并运用的!

《面试1v1》java多线程

最近我在更新《面试1v1》系列文章,主要以场景化的方式,讲解咱们在面试中遇到的问题,致力于让每一位工程师拿到自己心仪的offer,感兴趣能够重视JavaPub追更!

目录合集:

Gitee:https://gitee.com/rodert/JavaPub

GitHubhttps://github.com/Rodert/JavaPub