Java面试题06

1.并行和并发有什么区别?

        并行是指多个任务同时进行,每个任务有自己的执行线程;并发是指多个任务交替进行,通 过时间片轮转或优先级调度实现。

并行和并发是两种不同的执行方式,它们在执行时间、执行实体和目标上有所不同。

  1. 执行时间:并行是在同一时刻,有多条指令在多个处理器上同时执行。无论从微观还是从宏观来看,它们都是一起执行的。并发则是在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的。
  2. 执行实体:并行是在不同实体上的多个事件。并发则是在同一实体上的多个事件。

综上所述,并行和并发的主要区别在于执行时间、执行实体和目标上。并行是在同一时刻多个处理器上同时执行,而并发是在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行。并行编程和并发编程的目标都是充分利用处理器的资源,以达到最佳的处理性能。

2.线程和进程的区别?

线程和进程是操作系统中重要的概念,都是操作系统资源分配的基本单位,但它们之间有一些关键的区别。

  1. 地址空间:同一进程的线程之间可以共享进程的地址空间,不同进程的线程之间不能共享进程的地址空间。
  2. 资源拥有:同一进程内的线程共享本进程的地址空间和资源,但是进程之间的地址空间和资源是独立的。
  3. 通信:同一进程内的线程之间共享本进程数据空间和信息空间及实体资源,所以一般使用共享内存通信方式,速度快。不同进程间的通信一般使用消息传递方式。
  4. 开销:创建和销毁一个进程需要保存寄存器、栈信息、资源分配及回收等操作,开销较大。而线程的创建和销毁只需保存寄存器和栈信息,开销较小。
  5. 执行过程:一个程序至少有一个进程,一个进程至少有一个线程。线程不能独立执行,必须依存在应用程序中,应用程序至少有一个进程,这种进程就是主进程。
  6. 并发性:支持多线程的系统要比支持多进程的系统具有更高的并发性,从而使得开发与执行更高效。
  7. 独立性:进程是独立的,父进程和子进程都有各自独立的数据空间和代码。而线程不能独立运行(有进程占用内存才能有线程来完成操作),线程间共享相同的数据空间并共享系统资源。
  8. 并发控制:由于每个进程都独立于其他进程,因此它们不会互相干扰或影响。而线程则不同,由于它们共享一些资源,因此需要进行并发控制以确保资源的正确使用和数据的完整性。
  9. 稳定性:由于每个进程都独立于其他进程,因此一个进程崩溃不会影响其他进程的稳定性。然而,如果一个线程崩溃,它可能会影响到其他线程的稳定性。
  10. 效率:多进程的效率一般比多线程高,因为每个进程有自己的地址空间和资源,可以避免线程之间因共享资源而导致的竞争和等待问题。

总的来说,线程和进程在资源拥有、地址空间、通信、开销、执行过程、并发性、独立性、并发控制、稳定性和效率等方面存在明显的差异。选择使用线程还是进程取决于具体的应用场景和需求。在需要高并发和共享资源的情况下,使用线程是更合适的选择;而在需要独立运行和隔离的情况下,使用进程是更合适的选择。

3.守护线程是什么?

守护线程(Daemon Thread)是在程序后台提供服务的线程,当所有非守护线程结束时, 守护线程会自动终止。

守护线程(Daemon Thread)又被称为“服务进程”、“精灵线程”或“后台线程”,是指在程序运行时在后台提供一种通用服务的线程,这种线程并不属于程序中不可或缺的部分。守护线程通常用于执行一些辅助性的任务,如垃圾回收、内存管理等。

守护线程和普通线程的区别在于,守护线程的生命周期与程序的生命周期相互关联。当所有的非守护线程结束时,守护线程也会随之结束。与普通线程相比,守护线程不会阻塞程序的退出。当所有的非守护线程结束时,守护线程会自动退出,不管它是否执行完任务。

在Java中,守护线程是默认开启的,可以通过调用Thread类的setDaemon方法来设置一个线程为守护线程。如果一个Java程序中没有非守护线程,那么程序会立即退出。

4.创建线程有哪几种方式?

可以通过继承Thread类、实现Runnable接口、实现Callable接口、使用线程池等方式创建 线程。

在Java中,创建线程有四种方式:

  1. 通过继承Thread类创建线程。
  2. 通过实现Runnable接口创建线程。
  3. 通过实现Callable接口创建线程(使用Future)。
  4. 通过使用线程池创建线程,例如使用Executor框架。

以下是这四种方式的详细步骤和实现代码:

  1. 继承Thread类:创建一个继承自Thread类的子类,重写Thread类的run()方法(此线程执行的操作在run()中实现),创建Thread类的子类的对象,通过此对象调用start()启动线程,调用run()中的代码。
  2. 实现Runnable接口:创建一个实现Runnable接口的类,重写run()方法(此线程执行的操作在run()中实现),创建Thread类的对象并将Runnable接口的实现类的对象作为参数传递给Thread类的构造函数,调用Thread对象的start()方法启动线程,调用run()中的代码。
  3. 实现Callable接口(使用Future):创建一个实现Callable接口的类,重写call()方法(此线程执行的操作在call()中实现),创建ExecutorService对象并调用submit()方法提交任务,获取Future对象,调用get()方法获取结果。
  4. 使用线程池(使用Executor):创建一个ExecutorService对象,通过submit()方法提交任务,获取Future对象,调用get()方法获取结果。可以使用Executors类创建固定大小的线程池。

需要注意的是,在使用多线程时需要注意线程安全和并发控制的问题,例如避免线程间的竞争和死锁等问题。

5.说一下 runnable 和 callable 有什么区别?

Runnable用于定义一个线程任务,不返回结果;Callable也用于定义一个线程任务,但可 以返回结果,并且可以抛出异常。

Runnable和Callable是两种不同的接口,它们的主要区别在于返回值和异常处理。

  1. 返回值:Runnable接口中的方法没有返回值,而Callable接口中的方法有返回值。这意味着Runnable对象通过调用start()方法启动线程后,不能从run()方法获取返回值,而Callable对象通过调用call()方法执行任务后,可以获取返回值。
  2. 异常处理:Runnable接口中的方法没有抛出异常,而Callable接口中的方法可以抛出异常。这意味着在实现Runnable接口的类中,不需要处理异常,而实现Callable接口的类中,需要处理异常。

此外,Runnable和Callable的使用场景也有所不同。Runnable通常用于作为Thread的构造参数开启新的线程,而Callable则通常用于通过ExecutorService执行或者作为FeatureTask的参数。

总的来说,Runnable和Callable都是用于实现多线程的工具,它们在返回值和异常处理方面有所不同,使用时需要根据具体需求选择合适的接口。

6.线程有哪些状态?

线程有多个状态,包括New(新建)、Runnable(可运行)、Blocked(阻塞)、 Waiting(等待)、Timed Waiting(计时等待)和Terminated(终止)。

线程有以下几种状态:

  1. 新建状态(New):新创建了一个线程对象。

  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。

  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

    • 等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。
    • 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。

需要注意的是,不同的操作系统和编程语言可能会有不同的线程状态定义和转换规则,因此具体的状态和转换细节可能会有所不同。

7.sleep() 和 wait() 有什么区别?

sleep()方法是Thread类的方法,会让线程休眠一段时间;wait()方法是Object类的方法,会 让线程等待并释放锁。

sleep()和wait()都是Java中用于控制线程的函数,但它们在用途和机制上有一些不同。

  1. sleep():这个方法是Thread类的一部分,可以让当前线程暂停执行指定的时间。这个方法需要一个参数,即线程应该暂停的时间(以毫秒为单位)。当线程调用sleep()方法时,它会在当前位置停止执行,进入阻塞状态,直到指定的时间过去。在这个过程中,线程不会释放任何锁资源。

  2. wait():这个方法是Object类的一部分,用于线程之间的通信。当一个线程调用一个对象的wait()方法时,它会在当前位置停止执行,进入等待状态,直到其他线程调用同一个对象的notify()或notifyAll()方法。与sleep()不同,wait()方法需要在一个同步块或同步方法中调用,因为它涉及到对象的内部锁状态。

因此,sleep()主要用于让当前线程暂停一段时间,而wait()主要用于线程之间的通信和协调。在编程中,使用它们需要谨慎考虑线程的同步和通信问题。

8.notify()和 notifyAll()有什么区别?

notify()方法唤醒一个正在等待该对象锁的线程;notifyAll()方法唤醒所有正在等待该对象锁 的线程。

notify()和notifyAll()都是Object类的方法,用于唤醒在此对象上等待的线程。但是它们之间有一些重要的区别:

  1. notify()方法唤醒在此对象上等待的单个线程,如果多个线程在等待,则选择一个。被选中的线程将立即运行,如果没有线程在等待,则方法没有任何效果。

  2. notifyAll()方法唤醒在此对象上等待的所有线程,所有等待的线程都会被唤醒并立即运行。如果没有线程在等待,则方法没有任何效果。

因此,如果你想唤醒单个线程,可以使用notify();如果你想唤醒所有线程,可以使用notifyAll()。

9.线程的 run()和 start()有什么区别?

run()方法定义了线程的任务逻辑,直接调用会在当前线程中执行;start()方法启动线程,在 新的线程中执行run()方法。

run()和start()都是Java中用于控制线程的方法,但它们在用途和机制上有一些不同。

run()方法:

  1. 定义在Thread类中,是一个普通方法。
  2. 在调用start()方法之后,线程开始运行时会调用run()方法。
  3. 如果直接调用run()方法,那么这个线程就等于执行了run()方法中的代码,但并不会启动新的线程,而是会阻塞当前线程的执行。
  4. 如果在子类中重写run()方法,那么这个重写的方法就是该线程运行时执行的方法。

start()方法:

  1. 定义在Thread类中,是一个实例方法。
  2. 调用start()方法后,会启动一个新的线程,这个线程会执行run()方法中的代码。
  3. 调用start()方法后,当前线程不会等待新启动的线程执行完毕,而是会立即继续执行后面的代码。
  4. start()方法必须在线程的run()方法执行之前被调用,否则会抛出IllegalThreadStateException异常。

总结:run()方法是用来定义线程要执行的代码,而start()方法是用来启动新线程并执行run()方法中的代码。

10.创建线程池有哪几种方式?

可以使用Executors工厂类的静态方法创建线程池,或者直接使用ThreadPoolExecutor构造 函数

创建线程池的方式主要有以下几种:

  1. 通过ThreadPoolExecutor手动创建线程池。你可以根据需要,指定线程池的基本参数,如核心线程数、最大线程数、存活时间以及工作队列等,来创建并管理线程池。
  2. 通过Executors执行器自动创建线程池。Executors类提供了多种方法来创建线程池,包括固定大小的线程池、可缓存的线程池、定时线程池等。这些方法可以满足不同的应用需求。

以上两种方式可以满足大部分场景的线程池创建需求。但需要注意的是,创建线程池时需要考虑线程的安全性和并发控制的问题,避免出现线程间的竞争和死锁等问题。

你可能感兴趣的:(Java学习,java,jvm,开发语言)