前言 在之前的文章《Java面试通关宝典(一)》中,沉思君为大家介绍了几道常见的面试题与参考答案,有些题目还附有延伸问题,如果不清楚这些题目的思路,可以申请进群进行讨论,进群方式见文末。今天我们继续来探讨更多有趣的面试题,希望这些题目对大家找工作会有帮助。 面试题精选 1.说说对线程安全的理解 根据《Java并发编程实战》一书的定义,当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,则称这个类是线程安全的。如果在多个线程访问同一个可变的状态变量时没有使用合适的同步,那么就可能出现线程不安全问题,解决线程不安全问题通常有以下几种思路: 1)不在线程之间共享状态变量 这种思路有2种实现方式,第一种方式是将对象无状态化,无状态指的是该对象不包含任何属性,也不包含任何对其他对象的属性的引用;第二种方式是使用ThreadLocal,即线程本地化存储,使得变量不在各个线程中共享。这2种方式都不在多线程环境中共享变量,所以是线程安全的。 2)将状态变量设置为不可变变量 不可变对象指的是在创建后其状态就不能被修改的对象,其只有一个状态,并且该状态由构造函数来控制,因此是线程安全的。不可变对象需满足以下条件: 对象创建之后其状态就不能修改; 对象的所有属性都是final类型的; 对象是正确创建的(在创建对象期间,this引用没有逸出)。 3)在访问状态变量时使用同步 使用同步锁来保护状态变量,也能保证线程安全。这也是大多数人最熟悉的方式。 2.synchronized与Lock的区别 两者都可以作为同步锁来是保证线程安全。两者的主要区别如下: synchronized为非公平锁,而Lock可以选择使用公平锁还是非公平锁; synchronized在等待锁的过程中不能响应中断,而Lock可以响应中断; synchronized在等待锁时不能设置超时,而Lock可以设置超时; synchronized在出现异常时会自动释放锁,而Lock发生异常不会释放锁,所以使用Lock时一般会在finally块中释放锁; synchronized为独占锁,而Lock可以实现共享锁(如:读写锁)。 3.线程池中任务的执行策略 首先要讲一下线程池中的几个概念。 核心线程数:在线程池空闲时线程池中线程数的最大数量,如果向线程池中提交任务时,线程池中的线程数少于核心线程数,则会创建一个新的线程来执行该任务,即使其他线程是空闲的。如果发现线程池的线程数已经达到核心线程数,则将刚提交的任务放入到工作队列中。 工作队列:当线程池中的线程数到达核心线程数时,新提交的任务将会放到工作队列中排队等待。如果工作队列满了,而且线程池的线程数少于最大线程数,则会创建新的线程。 最大线程数:如果工作队列满了,而且线程池的线程数少于最大线程数,则会创建新的线程。如果线程池中的线程数量达到最大线程数,且每个线程都处于忙碌状态,则新提交的任务将会根据具体的饱和策略进行处理。 饱和策略:如果线程池中的线程数量达到最大线程数,或者线程池被shut down,则新提交的任务会根据具体的饱和策略进行处理。预定义的饱和策略有如下4种: 抛出异常; 由调用线程执行该任务; 直接将该任务丢弃; 丢弃工作队列中最早的任务,然后重新提交该任务。 最大空闲时间:如果线程池中的线程数超过核心线程数或者线程池被shutdown,则线程池会销毁那些空闲时间超过最大空闲时间的线程使得线程池中线程数降为核心线程数。 由于篇幅所限,后续再介绍更多经典的面试题。 |
|