多线程

image.png

1. 使用多线程有什么好处?

多线程有多种好处,如下所示:

  • 即使程序的一部分被阻塞,也允许程序继续运行。
  • 与使用多个进程的传统并行程序相比,提高了性能。
  • 允许编写利用最大 CPU 时间的有效程序
  • 提高复杂应用程序或程序的响应能力。
  • 增加 CPU 资源的使用并降低维护成本。
  • 节省时间和并行任务。
  • 如果在单个线程中发生异常,它不会影响其他线程,因为线程是独立的。
  • 与同时执行多个进程相比,资源密集型更少。

2. Java中的线程是什么?

线程基本上是可以由调度程序独立管理的轻量级和最小的处理单元。线程被称为进程的一部分,它只是让程序与进程的其他部分或线程同时有效地执行。使用线程,可以以最简单的方式执行复杂的任务。它被认为是利用机器中可用的多个 CPU 的最简单方法。它们共享公共地址空间并且彼此独立。

3、Java实现线程的两种方式是什么?

在java中实现线程基本上有两种方法,如下所示:

  • 扩展线程

例子:

class MultithreadingDemo extends Thread 
{   
  public void run() 
 {   
     System.out.println("My thread is in running state.");    
 } 
  public static void main(String args[]) 
 {   
    MultithreadingDemoobj=new MultithreadingDemo();  
        obj.start();  
  }  
} 

输出:

My thread is in running state.
  • 在 Java 中实现Runnable接口

示例

class MultithreadingDemo implements Runnable 
{  
   public void run() 
 {  
      System.out.println("My thread is in running state.");  
  }  
    public static void main(String args[]) 
 {  
      MultithreadingDemo obj=new MultithreadingDemo();   
      Threadtobj =new Thread(obj);       tobj.start();  
 }   
} 

输出:

My thread is in running state. 

4、线程和进程有什么区别?

线程:它只是指特定进程的最小单元。它具有同时执行程序的不同部分(称为线程)的能力。

进程:它只是指正在执行的程序,即活动程序。可以使用 PCB(过程控制块)处理过程。

image.png

线 过程
它是进程子单元的子集。 它是一个包含多个线程的正在执行的程序。
在这种情况下,线程间通信更快、更便宜、更容易、更高效,因为线程共享它们所属进程的相同内存地址。 在这种情况下,进程间通信更慢、更昂贵、更复杂,因为每个进程都有不同的内存空间或地址。
这些更容易创建、轻量级并且开销更少。 这些很难创建,重量级,并且有更多的开销。
它需要更少的时间来创建、终止和上下文切换。 它需要更多的时间来创建、终止和上下文切换。
具有多个线程的进程使用较少的资源。 没有线程的进程使用更多资源。
线程是进程的一部分,因此它们相互依赖,但每个线程独立执行。 进程相互独立。
需要在线程中进行同步以避免意外情况或问题。 每个进程都不需要同步。
他们彼此共享数据和信息。 他们不相互共享数据。

5. 类锁和对象锁有什么区别?

类锁:在java中,每个类都有一个唯一的锁,通常称为类级锁。这些锁是使用关键字“静态同步”实现的,可用于使静态数据线程安全。它通常在想要防止多个线程进入同步块时使用。

例子:

public class ClassLevelLockExample  
{    
  public void classLevelLockMethod()  
 {       
     synchronized (ClassLevelLockExample.class)  
       {         
            //DO your stuff here       
       }    
 } 
} 

对象锁:在java中,每个对象都有一个唯一的锁,通常称为对象级锁。这些锁是使用关键字“synchronized”实现的,可用于保护非静态数据。它通常在想要同步非静态方法或块时使用,以便只有线程能够在给定的类实例上执行代码块。

示例 :

public class ObjectLevelLockExample  
{    
  public void objectLevelLockMethod()  
 {   
     synchronized (this)  
       {     
            //DO your stuff here   
       } 
 }
} 

6、User线程和Daemon线程有什么区别?

用户和守护进程基本上是 Java 中使用“线程类”的两种类型的线程。

用户线程(非守护线程):在 Java 中,用户线程具有特定的生命周期,其生命周期独立于任何其他线程。JVM(Java 虚拟机)在终止之前等待任何用户线程完成其任务。当用户线程完成时,JVM 会终止整个程序以及相关的守护线程。

守护线程:在Java中,守护线程基本上被称为服务提供者,为用户线程提供服务和支持。守护线程的线程类中基本上有两种可用的方法:setDaemon() 和 isDaemon()。

用户线程与守护线程

用户线程 守护线程
JVM 在终止前等待用户线程完成其任务。 JVM 在终止前不会等待守护线程完成其任务。
这些线程通常由用户创建,用于并发执行任务。 这些线程通常由 JVM 创建。
它们用于应用程序的关键任务或核心工作。 它们不用于任何关键任务,而是用于执行一些辅助任务。
这些线程被称为高优先级任务,因此需要在前台运行。 这些线程被称为低优先级线程,因此特别需要支持后台任务,如垃圾收集、释放未使用对象的内存等。

7.我们如何创建守护线程?

我们可以使用线程类setDaemon(true)在 java 中创建守护线程。它用于将当前线程标记为守护线程或用户线程。isDaemon()方法一般用于检查当前线程是否为守护进程。如果线程是守护进程,它将返回 true,否则返回 false。
示例
**说明 setDaemon() 和 isDaemon() 方法使用的程序。 **

public class DaemonThread extends Thread 
{ 
   public DaemonThread(String name){ 
       super(name); 
   } 
   public void run() 
   {  
       // Checking whether the thread is Daemon or not 
       if(Thread.currentThread().isDaemon()) 
       {  
           System.out.println(getName() + " is Daemon thread");  
       }    
       else 
       {  
           System.out.println(getName() + " is User thread");  
       }  
   }   
   public static void main(String[] args) 
   {  
       DaemonThread t1 = new DaemonThread("t1"); 
       DaemonThread t2 = new DaemonThread("t2"); 
       DaemonThread t3 = new DaemonThread("t3");  
       // Setting user thread t1 to Daemon 
       t1.setDaemon(true);       
       // starting first 2 threads  
       t1.start();  
       t2.start();   
       // Setting user thread t3 to Daemon 
       t3.setDaemon(true);  
       t3.start();         
   }  
} 

输出:

t1 is Daemon thread 
t3 is Daemon thread 
t2 is User thread 

但是只能在start() 方法之前调用 setDaemon()方法,否则肯定会抛出 IllegalThreadStateException,如下所示:

public class DaemonThread extends Thread 
{ 
   public void run() 
   { 
       System.out.println("Thread name: " + Thread.currentThread().getName()); 
       System.out.println("Check if its DaemonThread: "  
                       + Thread.currentThread().isDaemon()); 
   } 
   public static void main(String[] args) 
   { 
       DaemonThread t1 = new DaemonThread(); 
       DaemonThread t2 = new DaemonThread(); 
       t1.start();         
       // Exception as the thread is already started 
       t1.setDaemon(true); 
       t2.start(); 
   } 
} 

输出:

Thread name: Thread-0 
Check if its DaemonThread: false 

8. wait() 和 sleep() 方法是什么?

wait():顾名思义,它是一种非静态方法,它会导致当前线程等待并进入睡眠状态,直到其他一些线程为对象的监视器(锁)调用 notify() 或 notifyAll() 方法。它只是释放锁,主要用于线程间通信。它在对象类中定义,并且只能从同步上下文中调用。

synchronized(monitor) 
{ 
monitor.wait();       Here Lock Is Released by Current Thread  
} 

sleep():顾名思义,它是一种静态方法,可以暂停或停止当前线程的执行一段时间。它在等待时不会释放锁,主要用于在执行时引入暂停。它在线程类中定义,无需从同步上下文中调用。

synchronized(monitor) 
{ 
Thread.sleep(1000);     Here Lock Is Held by The Current Thread 
//after 1000 milliseconds, the current thread will wake up, or after we call that is interrupt() method 
} 

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

notify():它发送一个通知并且只唤醒一个线程而不是多个线程在对象的监视器上等待。

image.png

notifyAll():它发送通知并唤醒所有线程并允许它们竞争对象的监视器而不是单个线程。

image.png

10. 为什么 Object 类中存在 wait()、notify() 和 notifyAll() 方法?

image.png

我们知道每个对象都有一个监视器,它允许线程持有对象上的锁。但是线程类不包含任何监视器。线程通常通过调用对象的wait()方法等待对象的监视器(锁),并使用notify()或notifyAll()方法通知正在等待同一锁的其他线程。因此,这三个方法仅在对象上调用,并允许所有线程与在该对象上创建的每个线程进行通信。

11.什么是Runnable和Callable接口?写出它们之间的区别。

这两个接口一般都用于封装需要由另一个线程执行的任务。但是它们之间有一些区别,如下所示:

运行接口:这个接口基本上从一开始就在 Java 中可用。它仅用于在并发线程上执行代码。
可调用接口:这个接口基本上是作为并发包的一部分引入的新接口。它解决了可运行接口的限制以及一些重大变化,如泛型、枚举、静态导入、变量参数方法等。它使用泛型来定义对象的返回类型。

public interface Runnable  
{   
  public abstract void run(); 
}  
public interface Callable  
{    
V call() throws Exception;  
} 

可运行接口与可调用接口

可运行接口 可调用接口
它不返回任何结果,因此不能抛出检查异常。 它返回一个结果,因此可以抛出异常。
它不能传递给 invokeAll 方法。 它可以传递给 invokeAll 方法。
它是在 JDK 1.0 中引入的。 它是在 JDK 5.0 中引入的,因此在 Java 5 之前不能使用它。
它只是属于 Java.lang。 它只是属于 java.util.concurrent。
它使用 run() 方法来定义任务。 它使用 call() 方法来定义任务。
要使用此接口,需要重写 run() 方法。 要使用此接口,需要重写 call() 方法。

12、Thread类的start()和run()方法是什么?

start():简单来说,start() 方法用于启动或开始执行新创建的线程。当调用 start() 方法时,会创建一个新线程,并且这个新创建的线程会执行保存在 run() 方法中的任务。一个人只能调用一次 start() 方法。

run():简单来说,run() 方法用于启动或开始执行同一个线程。当调用 run() 方法时,不会像 start() 方法那样创建新线程。该方法由当前线程执行。可以多次调用 run() 方法。

13.解释线程池?

线程池只是启动时预初始化或工作线程的集合,可用于执行任务并在完成时放回池中。它被称为池线程,其中创建了一组固定大小的线程。通过减少应用程序线程的数量并管理它们的生命周期,可以使用线程池来缓解性能问题。使用线程,可以提高性能并实现更好的系统稳定性。为了创建线程池,java.util.concurrent.Executors 类通常提供工厂方法。

14. join() 方法的目的是什么?

join()方法通常用于暂停当前线程的执行,除非并且直到调用 join 的指定线程死亡或完成。要停止一个线程运行直到另一个线程结束,可以使用此方法。它将一个线程执行的开始连接到另一个线程执行的结束。它被认为是线程类的最终方法。

15. 垃圾回收是什么意思?

垃圾回收基本上是一个自动管理内存的过程。它使用了几种 GC 算法,其中流行的一种包括 Mark 和 Sweep。该过程包括三个阶段,即标记、删除和压缩/复制。简单来说,垃圾收集器找到程序不再需要的对象,然后删除或移除这些未使用的对象以释放内存空间。

16. 解释死锁的含义以及何时会发生?

死锁,顾名思义,就是多个线程永远被阻塞的情况。它通常发生在多个线程持有不同资源的锁并等待其他资源完成其任务时。

上图显示了两个线程被永远阻塞的死锁情况。线程 1 持有对象 1 但需要对象 2 才能完成处理,而线程 2 持有对象 2 但首先需要对象 1。在这种情况下,它们都将永远保持锁定并且永远不会完成任务。

17.解释Java中的volatile变量?

volatile 变量基本上是一个关键字,用于确保和解决多线程编程中变量更改的可见性。此关键字不能与类和方法一起使用,而是可以与变量一起使用。它只是用于实现线程安全。如果将任何变量标记为易失性,那么所有线程都可以直接从主存而不是 CPU 缓存中读取其值,以便每个线程都可以获取变量的更新值。

18.线程之间如何通信?

线程可以使用三种方法进行通信,即wait()、notify() 和notifyAll()。

19、两个线程可以同时执行两种方法(静态和非静态)吗?

对的,这是可能的。如果两个线程都获得了不同对象的锁,那么它们可以同时执行而不会出现任何问题。

20. finalize() 方法的目的是什么?

Finalize() 方法基本上是 Object 类的一种方法,专门用于在垃圾回收之前对非托管资源执行清理操作。它根本不打算被称为普通方法。完成 finalize() 方法后,对象会自动销毁。

面向有经验的 Java 中的多线程面试问题

21.什么是同步过程?为什么要使用它?

同步基本上是 java 中的一个过程,它启用了一种简单的策略来避免线程干扰和内存一致性错误。此过程确保当一个线程尝试访问共享资源时,该资源一次只被一个线程使用。它可以通过以下三种不同的方式实现:

  • 通过同步方法
  • 通过同步块
  • 通过静态同步

语法:

synchronized (object) 
{        
   //statement to be synchronized 
} 

22. 什么是同步方法和同步块?应该首选哪一个?

同步方法:在此方法中,线程在进入同步方法时获取对象上的锁,并在离开该方法时正常或通过抛出异常释放锁。除非当前线程完成执行并释放锁,否则其他线程不能使用整个方法。当想要锁定特定方法的全部功能时,可以使用它。

Synchronized Block:在该方法中,线程在synchronized关键字后面的括号内的对象上获取锁,并在离开块时释放锁。除非同步块存在,否则没有其他线程可以获取锁定对象上的锁。当一个人想要保持程序的其他部分可供其他线程访问时,可以使用它。

同步块应该更受青睐,因为它可以提高特定程序的性能。它只锁定程序的某个部分(关键部分)而不是整个方法,因此导致争用较少。

23. 什么是线程饥饿?

线程饥饿基本上是一种情况或条件,即线程无法定期访问共享资源,因此无法继续或取得进展。这是因为其他线程优先级高,占用资源时间过长。这通常发生在没有获得 CPU 以继续执行的低优先级线程上。


image.png

24. 什么是活锁?当它发生时会发生什么?

与死锁类似,活锁也是另一个并发问题。在这种情况下,线程的状态在彼此之间发生变化而没有任何进展。线程不会被阻塞,但由于资源不可用而停止执行。

25.什么是阻塞队列?

BlockingQueue 基本上代表了一个线程安全的队列。生产者线程使用 put() 方法将资源/元素插入队列,除非它已满,而消费者线程使用 take() 方法从队列中获取资源,直到它变空。但是如果一个线程试图从一个空队列中出队,那么一个特定的线程将被阻塞,直到某个其他线程将一个项目插入队列,或者如果一个线程试图将一个项目插入一个已经满的队列,那么一个特定的线程将被阻塞,直到一些线程从队列中取出一个项目。

image.png

示例:

package org.arpit.java2blog; 

import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 

public class BlockingQueuePCExample { 

   public static void main(String[] args) { 

       BlockingQueue queue=new ArrayBlockingQueue<>(5); 
       Producer producer=new Producer(queue); 
       Consumer consumer=new Consumer(queue); 
       Thread producerThread = new Thread(producer); 
       Thread consumerThread = new Thread(consumer); 

       producerThread.start(); 
       consumerThread.start(); 

   } 

   static class Producer implements Runnable { 

       BlockingQueue queue=null; 

       public Producer(BlockingQueue queue) { 
           super(); 
           this.queue = queue; 
       } 

       @Override 
       public void run() { 

               try { 
                   System.out.println("Producing element 1"); 
                   queue.put("Element 1"); 
                   Thread.sleep(1000); 
                   System.out.println("Producing element 2"); 
                   queue.put("Element 2"); 
                   Thread.sleep(1000); 
                   System.out.println("Producing element 3"); 
                   queue.put("Element 3"); 
               } catch (InterruptedException e) { 

                   e.printStackTrace(); 
               } 
       } 
   } 

   static class Consumer implements Runnable { 

       BlockingQueue queue=null; 

       public Consumer(BlockingQueue queue) { 
           super(); 
           this.queue = queue; 
       } 

       @Override 
       public void run() { 

           while(true) 
           { 
               try { 
                   System.out.println("Consumed "+queue.take()); 
               } catch (InterruptedException e) { 
                   e.printStackTrace(); 
               } 
           } 
       } 

   } 
} 

输出

Producing element 1 
Consumed Element 1 
Producing element 2 
Consumed Element 2 
Producing element 3 
Consumed Element 3

26. 一个线程可以启动两次吗?

不,一旦线程启动并完成其执行,就根本不可能重新启动线程。线程只运行一次,如果您尝试第二次运行它,那么它将抛出运行时异常,即 java.lang.IllegalThreadStateException。

示例

public class TestThreadTwice1 extends Thread{   
public void run(){   
System.out.println(" thread is executing now........");   
}   
public static void main(String args[]){   
TestThreadTwice1 t1=new TestThreadTwice1();   
t1.start();   
t1.start();   
}   
}   

输出:

thread is executing now........ 
Exception in thread "main" java.lang.IllegalThreadStateException  

27. 解释上下文切换。

上下文切换基本上是多线程的一个重要特性。它被称为 CPU 从一个线程或进程切换到另一个线程或进程。它允许多个进程共享同一个 CPU。在上下文切换中,存储线程或进程的状态,以便稍后可以在需要时恢复线程的执行。

28. 什么是 CyclicBarrier 和 CountDownLatch?

CyclicBarrier 和 CountDownLatch 都是管理多线程编程所必需的。但是它们之间有一些区别,如下所示:

CyclicBarrier:它是一种使用某种算法同步线程处理的工具。它使一组线程相互等待,直到它们到达共同的执行点或共同的障碍点,然后让它们进一步继续执行。即使通过设置屏障破坏了屏障,也可以重复使用相同的 CyclicBarrier。

CountDownLatch:它是一个工具,它可以让主线程等待,直到强制操作被其他线程执行并完成。简而言之,它确保一个线程在开始执行之前等待另一个线程中的执行完成。一旦计数达到 0,就不能重用相同的 CountDownLatch。

29. 线程间通信是什么意思?

线程间通信,顾名思义,是一种进程或机制,多个线程可以使用该进程或机制相互通信。它特别用于避免 java 中的线程轮询,可以使用 wait()、notify() 和 notifyAll() 方法获得。

30. 什么是线程调度器和时间片?

线程调度器:它是 JVM 的一个组件,用于在多个线程等待获得执行机会的情况下决定下一个线程将执行。通过查看分配给每个 READY 线程的优先级,线程调度程序选择下一个运行来执行。线程调度主要使用两种机制:抢占式调度和时间片调度。

Time Slicing:它特别用于划分CPU时间并将它们分配给活动线程。在这种情况下,每个线程将获得一个预定义的时间片来执行。当时间到期时,特定线程必须等到其他线程有机会以循环方式使用它们的时间。每个正在运行的线程都会在固定的时间段内执行。

31.什么是关机钩子?

关闭挂钩只是在 JVM 关闭之前隐式调用的线程。它是 JVM 最重要的特性之一,因为它提供了进行资源清理或保存 JVM 关闭的应用程序状态的能力。通过调用 Runtime 类的 halt(int) 方法,可以停止关闭钩子。使用以下方法,可以添加关闭挂钩。

public void addShutdownHook(Thread hook){}     
Runtime r=Runtime.getRuntime();   
r.addShutdownHook(new MyThread());

32.什么忙转?

Busy Spinning,也称为Busy-waiting,是一种线程等待某些条件发生的技术,而不调用等待或睡眠方法并释放CPU。在这种情况下,可以通过让它运行一个空循环一段时间来暂停一个线程,它甚至不给 CPY 控制权。因此,它用于保留 CPU 缓存并避免重建缓存的成本。

33、什么是ConcurrentHashMap和Hashtable?在java中,为什么ConcurrentHashMap被认为比Hashtable快?

ConcurrentHashMap:它是在 Java 1.5 中引入的,用于使用多个存储桶存储数据。顾名思义,它允许对地图进行并发读写操作。它只在进行迭代时锁定映射的特定部分以提供线程安全,以便其他读取器仍然可以访问映射而无需等待迭代完成。

Hashtable:它是一个线程安全的遗留类,在旧版本的 java 中引入,用于使用哈希表存储键或值对。与 ConcurrentHashMap 不同,它不提供任何无锁读取。它只是在进行迭代时锁定整个地图。

ConcurrentHashMap 和 Hashtable 都是线程安全的,但与 Hashtable 不同,ConcurrentHashMap 通常会避免读锁并提高性能。与 Hashtable 不同,ConcurrentHashMap 还提供无锁读取。因此,ConcurrentHashMap 被认为比 Hashtable 更快,尤其是当读取器的数量比写入器的数量多时。

34.解释线程优先级。

线程优先级仅仅意味着具有最高优先级的线程将在低优先级线程之前获得执行的机会。可以指定优先级,但最高优先级线程不必在低优先级线程之前执行。线程调度程序根据线程优先级将处理器分配给线程。优先级范围从最低优先级到最高优先级在 1-10 之间变化。


image.png

35. Java中的ThreadLocal变量是什么意思?

ThreadLocal 变量是由 Java ThreadLocal 类创建和提供的特殊类型的变量。这些变量只允许被同一个线程读写。两个线程无法看到彼此的 ThreadLocal 变量,因此即使它们执行相同的代码,也不会出现任何竞争条件,并且代码将是线程安全的。

示例

public class ThreadLocalExp   
{   
     public static class MyRunnable implements Runnable    
   {   
       private ThreadLocal threadLocal =   
              new ThreadLocal();   
      @Override   
       public void run() {   
           threadLocal.set( (int) (Math.random() * 50D) );   
           try    
           {   
               Thread.sleep(1000);   
           } catch (InterruptedException e) {   
           }   
           System.out.println(threadLocal.get());   
       }   
   }   
   public static void main(String[] args)    
   {   
       MyRunnable runnableInstance = new MyRunnable();    
       Thread t1 = new Thread(runnableInstance);   
       Thread t2 = new Thread(runnableInstance);   
      // this will call run() method    
       t1.start();   
       t2.start();   
   }   
} 

输出

10 
33 
10 33 

36.什么是信号量?

信号量被认为是一种线程同步结构,通常需要使用计数器来控制和管理对共享资源的访问。它只是设置线程的限制。信号量类在包 java.util.concurrent 中定义,可用于在线程之间发送信号以避免丢失信号或保护临界区。它还可以用于实现资源池或有界集合。

37. 解释线程组。为什么我们不应该使用它?

ThreadGroup 是一个用于在单个对象中创建多组线程的类。这组线程以三种结构的形式存在,其中每个线程组都有一个父线程,但初始线程除外。线程组也可以包含其他线程组。一个线程只被允许访问关于它自己的线程组的信息,而不是其他线程组。

以前在旧版本的 Java 中,唯一没有线程组就无法工作的功能是 uncaughtException(Thread t, Throwable e)。但是现在在 Java 5 版本中,有了 Thread.setUncaughtExceptionHandler(UncaughtExceptionHandler)。所以现在即使没有线程组也可以工作,因此不需要使用线程组。

t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() 
{ 
@Override  
public void uncaughtException(Thread t, Throwable e)  
{  
System.out.println("exception occured:"+e.getMessage()); 
}  
}; 

38、什么是ExecutorService接口?

ExecutorService 接口基本上是 Executor 接口的子接口,具有一些额外的方法或特性,有助于管理和控制线程的执行。它使我们能够在线程上异步执行任务。


image.png

例子:

import java.util.concurrent.ExecutorService;   
import java.util.concurrent.Executors;   
import java.util.concurrent.TimeUnit;   

public class TestThread {   
                                   public static void main(final String[] arguments) throws InterruptedException {   
ExecutorService e = Executors.newSingleThreadExecutor();   

     try {   
       e.submit(new Thread());   
        System.out.println("Shutdown executor");   
        e.shutdown();   
        e.awaitTermination(5, TimeUnit.SECONDS);   
  } catch (InterruptedException ex) {   
       System.err.println("tasks interrupted");   
  } finally {   

        if (!e.isTerminated()) {   
           System.err.println("cancel non-finished tasks");   
     }   
        e.shutdownNow();   
        System.out.println("shutdown finished");   
  }   
  }   

  static class Task implements Runnable {   

     public void run() {   

        try {   
        Long duration = (long) (Math.random() * 20);   
           System.out.println("Running Task!");   
           TimeUnit.SECONDS.sleep(duration);   
     } catch (InterruptedException ex) {   
           ex.printStackTrace();   
     }   
  }   
 }          
}   

输出:

Shutdown executor 
shutdown finished

39、如果我们不重写线程类的run()方法会发生什么?

如果我们不覆盖 run() 方法,什么都不会发生。编译器不会显示任何错误。它将执行线程类的 run() 方法,我们不会得到任何输出,因为 run() 方法的实现是空的。

**例子: **

class MyThread extends Thread { 
  //don't override run() method 
} 
public class DontOverrideRun { 
  public static void main(String[] args) { 
         System.out.println("Started Main."); 
         MyThread thread1=new MyThread(); 
      thread1.start(); 
         System.out.println("Ended Main."); 
  } 
} 

输出:

Started Main. 
Ended Main.  

40.什么是锁接口?为什么使用锁接口比使用同步块更好。?

Lock 接口是在 Java 1.5 中引入的,一般用作同步机制,为阻塞提供重要的操作。

使用 Lock 接口优于 Synchronization 块的优点:

  • Lock 接口的方法,即 Lock() 和 Unlock() 可以在不同的方法中调用。这是锁接口相对于同步块的主要优势,因为同步块完全包含在单个方法中。
  • 与同步块不同,锁接口更灵活,确保等待时间最长的线程获得公平的执行机会。

41、是否可以直接调用run()方法启动一个新线程?

不,这根本不可能。您需要调用 start 方法来创建新线程,否则 run 方法不会创建新线程。相反,它将在当前线程中执行。

42. 在多线程编程中,每个线程都可以有自己的栈吗?

当然,这是可能的。在多线程编程中,每个线程在内存中维护自己独立的堆栈区域,因此每个线程彼此独立而不是相互依赖。

结论

43. 结论

总的来说,多线程是 Java 和现代软件开发的一个非常重要的部分。这对于提高程序的效率非常有帮助,同时也减少了存储资源的使用。在本文中,我们讨论了与多线程相关的重要面试问题以及面试中最常被问到的答案,并将帮助您破解面试。

推荐教程:
练习
Java 开发人员技能

你可能感兴趣的:(多线程)