面试官:你来说说进程和线程的区别吧?

面试官首先你来讲讲进程和线程的区别吧?

候选者:进程是系统进行资源分配和调度的独立单位,每一个进程都有它自己的内存空间和系统资源

候选者:进程实现多处理机环境下的进程调度,分派,切换时,都需要花费较大的时间和空间开销

候选者:为了提高系统的执行效率,减少处理机的空转时间和调度切换的时间,以及便于系统管理,所以有了线程,线程取代了进程了调度的基本功能

候选者:简单来说,进程作为资源分配的基本单位,线程作为资源调度的基本单位

面试官:你来说说进程和线程的区别吧?_第1张图片 

面试官那我们为什么要用多线程呢?你平时工作中用得多吗?

候选者:使用多线程最主要的原因是提高系统的资源利用率。

候选者:现在CPU基本都是多核的,如果你只用单线程,那就是只用到了一个核心,其他的核心就相当于空闲在那里了。

候选者:在平时工作中多线程是随时都可见的。

候选者:比如说,我们系统Web服务器用的是Tomcat,Tomcat处理每一个请求都会从线程连接池里边用一个线程去处理。

候选者:又比如说,我们用连接数据库会用对应的连接池 Druid/C3P0/DBCP…

候选者:…等等这些都用了多线程的。

候选者:上面这些框架已经帮我们屏蔽掉「手写」多线程的问题

面试官:你来说说进程和线程的区别吧?_第2张图片 

面试官嗯,了解,那你实际开发中有用过吗?

候选者:当然有了,在我所负责的系统也会用到多线程的。

候选者:比如说,现在要跑一个定时任务,该任务的链路执行时间和过程都非常长,我这边就用一个线程池将该定时任务的请求进行处理。

候选者:这样做的好处就是可以及时返回结果给调用方,能够提高系统的吞吐量。

// 请求直接交给线程池来处理
public void push(PushParam pushParam) {
  try {
    pushServiceThreadExecutor.submit(() -> {
      handler(pushParam);
    });
  } catch (Exception e) {
    logger.error("pushServiceThreadExecutor error, exception{}:", e);
  }
}

候选者:还有就是我的系统中用了很多生产者与消费者模式,会用多个线程去消费队列的消息,来提高并发度

面试官:你如果在项目中用到了多线程,那肯定得考虑线程安全的问题的吧

面试官要不你来讲讲什么是线程安全?

候选者:在我的理解下,在Java世界里边,所谓线程安全就是多个线程去执行某类,这个类始终能表现出正确的行为,那么这个类就是线程安全的。

候选者:比如我有一个count变量,在service方法不断的累加这个count变量。

public class UnsafeCountingServlet extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        ++count;
        // To something else...
    }
}

候选者:假设相同的条件下,count变量每次执行的结果都是相同,那我们就可以说是线程安全的。

候选者:显然上面的代码肯定不是线程安全的。

面试官:你来说说进程和线程的区别吧?_第3张图片 

面试官那你平时是怎么解决,或者怎么思考线程安全问题的呢?

候选者:其实大部分时间我们在代码里边都没有显式去处理线程安全问题,因为这大部分都由框架所做了。

候选者:正如上面提到的Tomcat、Druid、SpringMVC等等。

候选者:很多时候,我们判断是否要处理线程安全问题,就看有没有多个线程同时访问一个共享变量。

候选者:像SpringMVC这种,我们日常开发时,不涉及到操作同一个成员变量,那我们就很少需要考虑线程安全问题。

候选者:我个人解决线程安全问题的思路有以下

  • 能不能保证操作的原子性,考虑atomic包下的类够不够我们使用。

  • 能不能保证操作的可见性,考虑volatile关键字够不够我们使用

  • 如果涉及到对线程的控制(比如一次能使用多少个线程,当前线程触发的条件是否依赖其他线程的结果),考虑CountDownLatch/Semaphore等等。

  • 如果是集合,考虑java.util.concurrent包下的集合类。

  • 如果synchronized无法满足,考虑lock包下的类

  • ….

面试官:你来说说进程和线程的区别吧?_第4张图片 

候选者:总的来说,就是先判断有没有线程安全问题,如果存在则根据具体的情况去判断使用什么方式去处理线程安全的问题。

候选者:虽然synchronized很牛逼,但无脑使用synchronized会影响我们程序的性能的。

面试官死锁你了解吗?什么情况会造成死锁?要是你能给我讲清楚死锁,我就录取你了

候选者:要是你录取我,我就给你讲清楚死锁

面试官:那我还是继续面面吧

候选者:开始你的表演吧

候选者:造成死锁的原因可以简单概括为:当前线程拥有其他线程需要的资源,当前线程等待其他线程已拥有的资源,都不放弃自己拥有的资源。

候选者:避免死锁的方式一般有以下方案:

  1. 固定加锁的顺序,比如我们可以使用Hash值的大小来确定加锁的先后

  2. 尽可能缩减加锁的范围,等到操作共享变量的时候才加锁。

  3. 使用可释放的定时锁(一段时间申请不到锁的权限了,直接释放掉)

面试官:你来说说进程和线程的区别吧?_第5张图片 

面试官:了解了

你可能感兴趣的:(Java面试系列,java)