zoukankan      html  css  js  c++  java
  • JAVA面试常见问题之进程和线程篇

    1、线程和进程的概念、并行和并发的概念

    • 进程:计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
    • 线程:有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

                

    • 并行:多个线程可以同时执行,每一个时间段,可以有多个线程同时执行。

              

    • 并发多个线程同时竞争一个位置,竞争到的才可以执行,每一个时间段只有一个线程在执行。

             

     2、创建线程的方式及实现

           创建线程的三种方式及实现代码:

    1. 继承Thread类创建
    public class MyThread extends Thread{//继承Thread类
      public void run(){
      //重写run方法
      }
    }
    
    public class Main {
      public static void main(String[] args){
        new MyThread().start();//创建并启动线程
      }
    }
    

             2. 实现Runnable接口创建

    public class MyThread2 implements Runnable {//实现Runnable接口
      public void run(){
      //重写run方法
      }
    }
    
    public class Main {
      public static void main(String[] args){
        //创建并启动线程
        MyThread2 myThread=new MyThread2();
        Thread thread=new Thread(myThread);
        thread().start();
        //或者    new Thread(new MyThread2()).start();
      }
    }
    

              3. 使用Callable和Futura创建

     3、进程间通信的方式

     进程间的通信(IPC)常用方式管道(无名管道和命名管道)、消息队列、信号量、共享存储、套接字(Socket)及Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

    • 管道
      • 无名管道管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。它是半双工的,具有固定的读端和写端。只能用于亲缘关系间的进程之间的通信。
      • FIFO:也称命名管道,它是一种文件类型。可以在无关的进程之间交换数据。有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
    • 消息队列是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。特点
      • 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
      • 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
      • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
    • 信号量:是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。特点
      • 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存
      • 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
      • 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
      • 支持信号量组。
    • 共享存储:指两个或多个进程共享一个给定的存储区。特点
      • 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
      • 因为多个进程可以同时操作,所以需要进行同步。
      • 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
    • 总结
      • 无名管道:速度慢,容量有限,只有父子进程能通讯
      • FIFO:任何进程间都能通讯,但速度慢
      • 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
      • 信号量:不能传递复杂消息,只能用来同步
      • 共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存

    4、说说 CountDownLatch、CyclicBarrier 原理和区别

       原理: 参考:https://blog.csdn.net/wantflydacheng/article/details/81664035

        区别

     5、说说 Semaphore 工作原理,举例说明

           Semaphore主要用于控制当前活动线程数目,就如同停车场系统一般,而Semaphore则相当于看守的人,用于控制总共允许停车的停车位的个数,而对于每辆车来说就如同一个线程,线程需要通过acquire()方法获取许可,而release()释放许可。如果许可数达到最大活动数,那么调用acquire()之后,便进入等待队列,等待已获得许可的线程释放许可,从而使得多线程能够合理的运行

    6、说说 Exchanger 原理

           Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据, 如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。因此使用Exchanger的重点是成对的线程使用exchange()方法,当有一对线程达到了同步点,就会进行交换数据。因此该工具类的线程对象是成对的。
           Exchanger类提供了两个方法,String exchange(V x):用于交换,启动交换并等待另一个线程调用exchange;String exchange(V x,long timeout,TimeUnit unit):用于交换,启动交换并等待另一个线程调用exchange,并且设置最大等待时间,当等待时间超过timeout便停止等待。
           参考:https://blog.csdn.net/carson0408/article/details/79477280

    7、ThreadLocal 原理分析,ThreadLocal为什么会出现OOM,出现的深层次原理

    • ThreadLocal是本地线程变量,用于保存某个线程的共享变量。方法包含get(),set(),remove(),initialValue()
    • 不正当的使用ThreadLocal时候会出现OOM,线程池的一个线程使用完ThreadLocal对象之后,再也不用,由于线程池中的线程不会退出,线程池中的线程的存在,同时ThreadLocal变量也会存在,占用内存!造成OOM溢出!
    • 出现OOM的深层次原理 参考:https://mp.weixin.qq.com/s/xqb1kUVtD82JuvlqWGV18w   第二部分  由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏。使用线程池,线程不会被销毁,ThreadLocalMap也不会销毁,随着线程数量也来越多,ThreadLocalMap也会越来越多,占得内存越来越大,从而导致出现内存溢出。

    8、讲讲线程池的实现原理

    一个线程集合workerSet和一个阻塞队列workQueue。当用户向线程池提交一个任务(也就是线程)时,线程池会先将任务放入workQueue中。workerSet中的线程会不断的从workQueue中获取线程然后执行。当workQueue中没有任务的时候,worker就会阻塞,直到队  列中有任务了就取出来继续执行。

    几个重要参数意义:

    • corePoolSize: 规定线程池有几个线程(worker)在运行。
    • maximumPoolSize: 当workQueue满了,不能添加任务的时候,这个参数才会生效。规定线程池最多只能有多少个线程(worker)在执行。
    • keepAliveTime: 超出corePoolSize大小的那些线程的生存时间,这些线程如果长时间没有执行任务并且超过了keepAliveTime设定的时间,就会消亡。
    • unit: 生存时间对于的单位
    • workQueue: 存放任务的队列
    • threadFactory: 创建线程的工厂
    • handler: 当workQueue已经满了,并且线程池线程数已经达到maximumPoolSize,将执行拒绝策略。

    9、线程池的几种实现方式

    1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
    4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    10、线程的生命周期,状态是如何转移的

    生命周期新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态

    状态转移

    1. 新建状态,当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值

    2. 就绪状态,当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行

    3. 运行状态,如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态

    4. 阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态

    5. 在线程的生命周期当中,线程的各种状态的转换过程

    参考:https://www.cnblogs.com/sunddenly/p/4106562.html    

         另有线程相关面试题:https://mp.weixin.qq.com/s/rW0H5oez1IfMOtaPBwWGoA(可能和上面的有重复)


               

     

  • 相关阅读:
    ubuntu解压时中文出现乱码
    centos7下搭建高匿HTTP代理
    nmon监控分析
    oracle12c
    Linux下卸载Oracle 11g
    Oracle 11g修改字符集
    centos下静默安装oracle11g
    Hive基础之Hive开启查询列名及行转列显示
    Hive基础之HiveServer2 JDBC的使用
    JavaScript对象的创建总结
  • 原文地址:https://www.cnblogs.com/lu51211314/p/10213799.html
Copyright © 2011-2022 走看看