面试官惊叹,你这多线程基础可以啊(上)

简介: 一、多线程理论二、实现线程的方式三、线程常用的方法

网络异常,图片无法展示
|


「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」


一、多线程理论


1.1、操作系统的发展

   在计算机发明之前,人们处理大量的计算是通过人工处理的,耗费人力,成本很大而且错误较多。为了处理大量的数学计算问题,人们发明了计算机。


   最初的计算机只能接受一些特定的指令,用户输入一个指令,计算机就做出一个操作。当用户在思考或者输入时,计算机就在等待。显然这样效率低下,在很多时候,计算机都处在等待状态。


1.1.1、批处理操作系统


 既然传统计算机那么慢,那么能不能把一系列需要操作的指令写下来,形成一个清单,一次性交给计算机,计算机通过不断得读取指令进行相应的操作。

 就这样,批处理操作系统诞生了。用户将多个需要执行的程序写在磁带上,然后交由计算机去读取并逐个执行这些程序,并将输出结果写在另一个磁带上。


1.1.2、如何提高CPU利用率


   虽然批处理操作系统的诞生提高了任务处理的便捷性(省略了用户输入的时间),但是仍然存在一个很大的问题:


   假如有两个任务A和B,需要读取大量的数据输入(I/O操作),而其实CPU只能处在等待状态,等任务A读取完数据再能继续进行,这样就白白浪费了CPU资源。于是人们就想,能否在任务A读取数据的过程中,让任务B去执行,当任务A读取完数据之后,暂停任务B,让任务A继续执行?


   这时候又出现了几个问题:内存中始终都只有一个程序在运行,而想要解决上述问题,必然要在内存中装入多个程序,如何处理呢?多个程序使用的数据如何辨别?当一个程序暂停后,随后怎么恢复到它之前执行的状态呢?


1.1.3、进程来了


   这时候,人们就发明了进程,用一个进程对应一个程序,每个进程都对应一定的内存地址和内存空间,并且只能自己使用自己的内存空间,多个进程之间的内存互不共享,且进程之间彼此不打扰。


   进程同时也保存了程序每时每刻的运行状态,为进程切换提供了如可能。


   当进程暂停时,它会保存当前进程的状态(进程标识,进程使用的资源等),在下一次切换回来时根据之前保存的2状态进行恢复,接着继续执行。


1.2、并发和并行


1.2.1、并发


   并发是能够让操作系统从宏观上看起来同一时间段执行多个任务。 换句话说,进程让操作体统的并发成为了可能,至此出现多任务操作系统。


   虽然并发从宏观上看是有多个任务在执行,但是实际上对于单核CPU来说,任意具体时刻都只有一个任务在占用CPU资源,操作系统一般通过CPU时间片轮转来实现并发。


   总的来说,并发就是在一段时间内多个进程轮流使用同一个 CPU,多个进程形成并发。

网络异常,图片无法展示
|


1.2.2、并行


   在同一时刻多个进程使用各自的 CPU多个进程形成并行。并行需要多个 CPU 支持。

网络异常,图片无法展示
|


1.3、线程


1.3.1、线程出现的原因


   出现了进程之后,操作系统的性能(CPU利用率)得到了大大的提升。虽然进程的出现解决了操作系统的并发问题,但是人们不满足,逐渐对实时性有了要求。因为一个进程在一个时间段内只能做一个事情,如果一个进程有多个子任务时,只能逐个得执行这些子任务,很影响效率。


   举一个例子:对于监控系统这个进程来说,不仅要与服务器端进行通信获取图像数据并将图像信息显示在画面上,还要处理与用户的交互操作。如果在一个时刻该系统正在与服务器通信获取图像数据,而用户在监控系统上点击了一个按钮,那么系统只能等获取完图像后才能与用户进行交互操作。如果获取图像需要10s,用户就得等待10s。显然这样的系统,无法满足人们的需求。


1.3.2、线程


   为了让子任务可以分开执行,即上个例子说的,在与服务器通信获取图形数据的同时相应用户,为了处理这种情况,人们发明了线程,一个线程执行一个子任务,这样一个进程就包含了多个线程,每个线程负责一个单独的子任务。在用户点击按钮的时候,可以暂停获取图像数据的线程,让出CPU资源,让UI线程获取CPU资源,响应用户的操作,响应完后再切换回来,获取图像数据的线程重新获取CPU资源。让用户感觉系统在同时做很多事,满足用户对实时性的要求。线程的出现是为了解决实时性的问题


   总的来说,线程是进程的细分,通常,在实时性操作系统中,进程会被划分为多个可以独立运行的子任务,这些子任务被称为线程,多个线程配合完成一个进程的任务


注意

 

  一个进程包含多个线程,但是这些线程共享进程占有的内存地址空间和资源。进程是操作系统进行资源分配的基本单位(进程之间互不干扰),而线程是操作系统进行CPU调度的基本单位(线程间互相切换)。

网络异常,图片无法展示
|


1.3.3、线程工作的原理


   假设 P 进程抢占 CPU 后开始执行,此时如果 P 进行正在进行获取网络资源的操作时,用户进行UI 操作,此时 P 进程不会响应 UI 操作。可以把 P 进程可以分为 Ta、Tb 两个线程。Ta 用于获取网络资源,Tb 用于响应 UI 操作。此时如果 Ta 正在执行获取网络资源时、用户进行 UI 操作,为了做到实时性,Ta 线程暂时挂起,Tb 抢占 CPU 资源,执行 UI 操作,UI 操作执行完成后让出CPU,Ta 抢占 CPU 资源继续执行请求网络资源。


总结


 1.线程再一次提高了CPU的利用率

  1. 线程是包含在进程中,是对进程任务的细分,线程共享进程资源(内存资源等)
  2. 线程细分后称为 CPU 调度的基本单位。进程称为操作系统资源分配的基本单位。

1.4、线程和进程的区别


  1. 根本区别:进程是操作系统资源分配的基本单位,而线程是CPU调度和执行的基本单位
  2. 在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
  3. 所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行
  4. 内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源
  5. 包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分


1.5、线程调度


1.5.1、分时调度


   所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。


1.5.2、抢占式调度



   优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。


二、实现线程的方式


   在 Java 中实现线程的方式有 2 种,一种是继承 Thread,一种是实现 Runnable 接口。


   如果一个进程没有任何线程,我们成为单线程应用程序;如果一个进程有多个线程存在,我们成为多线程应用程序。进程执行时一定会有一个主线程(main 线程)存在,主线程有能力创建其他线程。多个线程抢占 CPU,导致程序的运行轨迹不确定。多线程的运行结果也不确定。


2.1、继承Thread类


   线程开启我们需要用到了java.lang.Thread类,API中该类中定义了有关线程的一些方法,具体如下:


构造方法


  • public Thread():分配一个新的线程对象。
  • public Thread(String name):分配一个指定名字的新的线程对象。
  • public Thread(Runnable target):分配一个带有指定目标新的线程对象。
  • public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。


常用方法


  • public String getName():获取当前线程名称。
  • public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。
  • public void run():此线程要执行的任务在此处定义代码。
  • public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
  • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。


   继承 Thread 实现多线程,必须重写 run 方法启动的时候调用的也是调用线程对象的start()方法来启动该线程,如果直接调用run()方法的话,相当于普通类的执行,此时相当于只有主线程在执行。



package day16_thread.classing.thread;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 11:40
 */
public class MyThread extends Thread{
  @Override
  public void run() {
    for (int i =1;i<501;i++){
      System.out.println("A Thread"+i);
    }
  }
}


package day16_thread.classing.thread;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 11:41
 */
public class TestThread {
  public static void main(String[] args) {
    MyThread myThread = new MyThread();
    myThread.start();
    for (int i=1;i<501;i++){
      System.out.println("MainThread"+i);
    }
  }
}


网络异常,图片无法展示
|


   从结果我们可以看出,每一次抢占CPU资源的线程是不同的,多个线程轮流使用 CPU,谁先抢占到谁使用 CPU 并执行线程。所以执行结果不确定。


2.1.1、继承Thread类的优点


编码简单


2.1.2、继承Thread类的缺点


   线程类已经继承了Thread类了就无法再继承其他类了,功能不能通过其他类继承拓展,功能没有那么强大。


2.2、实现 Runnable 接口


   采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。

步骤如下:

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  3. 调用线程对象的start()方法来启动线程。


package day16_thread.classing.thread;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 13:49
 */
public class MyRun implements Runnable {
  @Override
  public void run() {
    for (int i =1;i<501;i++){
      System.out.println("A Thread"+i);
    }
  }
}


package day16_thread.classing.thread;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 13:49
 */
public class TestMyRun {
  public static void main(String[] args) {
    Thread thread = new Thread(new MyThread());
    thread.start();
    for (int i=1;i<501;i++){
      System.out.println("MainThread"+i);
    }
  }
}


网络异常,图片无法展示
|


2.2.1、实现Runnable的接口的优点

  1. 线程任务类只是实现了Runnable接口,可以继续继承其他类,而且可以继续实现其他接口(避免了单继承的局限性)
  2. 同一个线程任务对象可以被包装成多个线程对象
  3. 适合多个多个线程去共享同一个资源
  4. 实现解耦操作,线程任务代码可以被多个线程共享,线程任务代码和线程独立。
  5. 线程池可以放入实现Runable或Callable线程任务对象。
  6. 其实Thread类本身也是实现了Runnable接口的。
  7. 唯一的遗憾是不能直接得到线程执行的结果!


2.3、实现Callable接口(拓展)


   实现多线程还有另一种方式,那就是实现Callable接口,前面的两种方式都没办法拿到线程执行返回的结果,因为run()方法都是void修饰的。但是这种方式是可以拿到线程执行返回的结果。


步骤

  1. 定义一个线程任务类实现Callable接口 , 申明线程执行的结果类型。
  2. 重写线程任务类的call方法,这个方法可以直接返回执行的结果。
  3. 创建一个Callable的线程任务对象。
  4. 把Callable的线程任务对象包装成一个未来任务对象。
  5. 把未来任务对象包装成线程对象。
  6. 调用线程的start()方法启动线程。


package day16_thread.classing.thread;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 13:49
 */
// 1.创建一个线程任务类实现Callable接口,申明线程返回的结果类型
class MyCallable implements Callable<String>{
    // 2.重写线程任务类的call方法!
    @Override
    public String call() throws Exception {
        // 需求:计算1-10的和返回
        int sum = 0 ;
        for(int i = 1 ; i <= 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+" => " + i);
            sum+=i;
        }
        return Thread.currentThread().getName()+"执行的结果是:"+sum;
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        // 3.创建一个Callable的线程任务对象
        Callable call = new MyCallable();
        // 4.把Callable任务对象包装成一个未来任务对象
        //      -- public FutureTask(Callable<V> callable)
        // 未来任务对象是啥,有啥用?
        //      -- 未来任务对象其实就是一个Runnable对象:这样就可以被包装成线程对象!
        //      -- 未来任务对象可以在线程执行完毕之后去得到线程执行的结果。
        FutureTask<String> task = new FutureTask<>(call);
        // 5.把未来任务对象包装成线程对象
        Thread t = new Thread(task);
        // 6.启动线程对象
        t.start();
        for(int i = 1 ; i <= 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+" => " + i);
        }
        // 在最后去获取线程执行的结果,如果线程没有结果,让出CPU等线程执行完再来取结果
        try {
            String rs = task.get(); // 获取call方法返回的结果(正常/异常结果)
            System.out.println(rs);
        }  catch (Exception e) {
            e.printStackTrace();
        }
    }
}


2.3.1、实现Callable接口优点


  1. 线程任务类只是实现了Callable接口,可以继续继承其他类,而且可以继续实现其他接口(避免了单继承的局限性)
  2. 同一个线程任务对象可以被包装成多个线程对象
  3. 适合多个多个线程去共享同一个资源
  4. 实现解耦操作,线程任务代码可以被多个线程共享,线程任务代码和线程独立。
  5. 线程池可以放入实现Runable或Callable线程任务对象。
  6. 能直接得到线程执行的结果!
  7. 唯一的遗憾就是编码比较复杂,写的代码会比较多。

2.4、两种实现方式的区别


需求:模拟售票窗口买票的过程,共有五张票


2.4.1、Thread实现


package day16_thread.classing.thicks;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 13:53
 */
public class MyThread extends Thread{
  private static int count = 5;
  public MyThread() {
  }
  public MyThread(String name) {
    super(name);
  }
  @Override
  public void run() {
    for (int i=0;i<5;i++){
      if (count>0){
        count--;
        System.out.println(super.getName()+"卖了一张票。还剩下"+count+"张票");
      }
    }
  }
}
package day16_thread.classing.thicks;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 13:55
 */
public class TestThread {
  public static void main(String[] args) {
    MyThread t1 = new MyThread("窗口A");
    MyThread t2 = new MyThread("窗口B");
    MyThread t3 = new MyThread("窗口C");
    MyThread t4 = new MyThread("窗口D");
    t1.start();
    t2.start();
    t3.start();
    t4.start();
  }
}


网络异常,图片无法展示
|


2.4.2、Runable实现


package day16_thread.classing.thicks;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 14:15
 */
public class MyRun implements Runnable {
  private int count = 5;
  @Override
  public void run() {
    for (int i=0;i<5;i++){
      if (count>0){
        count--;
        System.out.println(Thread.currentThread().getName()+"卖了一张票。还剩下"+count+"张票");
      }
    }
  }
}
package day16_thread.classing.thicks;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 14:17
 */
public class TestRun {
  public static void main(String[] args) {
    MyRun myRun = new MyRun();
    Thread t1 = new Thread(myRun,"窗口A");
    Thread t2 = new Thread(myRun,"窗口B");
    Thread t3 = new Thread(myRun,"窗口C");
    Thread t4 = new Thread(myRun,"窗口D");
    t1.start();
    t2.start();
    t3.start();
    t4.start();
  }
}


网络异常,图片无法展示
|


2.4.3、两者实现的区别


  1. 继承Thread类后,不能再继承其他类,而实现了Runnable接口后还可以继承其他类。
  2. 实现Runnable接口更方便共享资源,同一份资源,多个线程并发访问,如果多个线程需要访问共享资源,优先考虑Runnable方式,如果线程不访问共享资源,可以考虑继承Thread。
  3. Thread类本身也是实现类Runnable接口的。


实现Runnable接口比继承Thread类所具有的优势:


  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免Java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池可以放入实现Runable或Callable类线程。


2.5、存在的问题


   多线程访问共享资源的同时,存在一个十分严重的问题,那就是会导致共享资源数据错乱。


2.6、多线程执行轨迹分析


假设我们拿一种执行情况来分析


网络异常,图片无法展示
|


网络异常,图片无法展示
|


2.7、总结


  1. 线程通过抢占CPU的方式工作,在执行过程中,随时可能CPU时间片的时间到了,然后被挂起,在程序的任何地方都有可能被切换出去
  2. 由于随时被挂起或者切换出CPU,导致访问共享资源会出现数据错乱,解决方法为加锁


三、线程常用的方法


3.1、设置线程优先级


   我们可以设置线程的优先级调用,优先级越高 ,被 CPU 调动的可能性越大,但不一定是优先级越高就一定先执行。,有可能设置了最高的优先级但是确实最后调用。


//系统的默认三种优先级
System.out.println(Thread.MAX_PRIORITY);//数字是10
System.out.println(Thread.MIN_PRIORITY);//数字是1
System.out.println(Thread.NORM_PRIORITY);//数字是5
package day16_thread.classing.PriorityTest;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 19:30
 */
public class TestPriority {
  public static void main(String[] args) {
    PriorityThrea p1 = new PriorityThrea("线程1");
    PriorityThrea p2 = new PriorityThrea("线程2");
    p1.setPriority(PriorityThrea.MAX_PRIORITY);
    p2.setPriority(PriorityThrea.MIN_PRIORITY);
    p1.start();
    p2.start();
  }
}


3.2、线程的强制执行


   强制执行(join方法)会导致其他线程阻塞,当线程执行完以后,其他线程阻塞原因消除,进入就绪状态。


package day16_thread.classing.join;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 15:45
 */
public class TestMyJoinThread {
  public static void main(String[] args) {
    MyJoinThread myJoinThread = new MyJoinThread();
    myJoinThread.start();
    for (int i =0;i<5;i++){
      System.out.println("main -> " + i);
      if (i==2){
        try {
          myJoinThread.join();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
}
复制代码


3.3、线程休眠


   线程调用(sleep方法)方法,传入一个毫秒值,会导致当前线程进入阻塞状态,阻塞时间到了以后线程进入就绪状态,sleep方法会抛出一个编译时异常InterruptedException


3.3.1、正常执行


package day16_thread.classing.sleep;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 19:47
 */
public class SleepThread extends Thread{
  public SleepThread() {
  }
  public SleepThread(String name) {
    super(name);
  }
  @Override
  public void run() {
    System.out.println("线程A开始执行");
    try {
      sleep(2000);
      System.out.println("休眠结束");
    } catch (InterruptedException e) {
      System.out.println("外界有程序中断线程 A");
    }
    System.out.println("线程A即将结束");
  }
}
package day16_thread.classing.sleep;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 19:50
 */
public class TestSleepThread {
  public static void main(String[] args) {
    SleepThread s = new SleepThread();
    s.start();
  }
}


3.3.2、异常情况


package day16_thread.classing.sleep;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 19:47
 */
public class SleepThread extends Thread{
  public SleepThread() {
  }
  public SleepThread(String name) {
    super(name);
  }
  @Override
  public void run() {
    System.out.println("线程A开始执行");
    try {
      sleep(20000);
      System.out.println("休眠结束");
    } catch (InterruptedException e) {
      System.out.println("外界有程序中断线程 A");
    }
    System.out.println("线程A即将结束");
  }
}
package day16_thread.classing.sleep;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 19:50
 */
public class TestSleepThread {
  public static void main(String[] args) {
    SleepThread s = new SleepThread();
    s.start();
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    //线程中断
    s.interrupt();
    System.out.println("主线程结束");
  }
}


网络异常,图片无法展示
|


3.3.3、总结


  1. 线程休眠导致当前线程进入阻塞状态,休眠时间结束后,线程进入就绪状态,抢占CPU,抢到后继续运行
  2. 线程休眠过程中可以被中断,所以存在一个编译时异常:InterruptedException,外界程序中断该线程时,休眠时间提前结束,进入就绪状态,等待CPU调度执行。


3.4、线程的礼让


package day16_thread.classing.yield;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 20:00
 */
public class YieldThread extends Thread{
  public YieldThread() {
  }
  public YieldThread(String name) {
    super(name);
  }
  @Override
  public void run() {
    for (int i = 0; i < 10; i++) {
      System.out.println(super.getName() + "=>" + i);
    }
  }
}
package day16_thread.classing.yield;
/**
 * @author Xiao_Lin
 * @date 2020/12/20 20:01
 */
public class TestThreadYield {
  public static void main(String[] args) {
    System.out.println("主线程开始执行");
    YieldThread y1 = new YieldThread();
    y1.start();
    for (int i = 0;i<1000;i++){
      System.out.println(Thread.currentThread().getName()+"->"+i);
      if (i%2 == 0){
        Thread.yield();
      }
    }
  }
}

当前线程礼让后,线程进入就绪状态。


3.5、线程结束

   stop表示强制停止一个线程,停止一个线程的风险较大,不建议使用,通过interrupt发送中断信号中断线程,线程就会在在那个时间点结束


   interrupt中止正在运行的线程,该线程不会立即结束,而是继续执行,在适当的时机选择结合异常处理机制结束,异常处理机制可以保证线程继续执行,通过异常处理机制让一个线程正常结束

相关文章
|
3月前
|
监控 Kubernetes Java
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
|
3月前
|
安全 Java 程序员
面试必看:如何设计一个可以优雅停止的线程?
嘿,大家好!我是小米。今天分享一篇关于“如何停止一个正在运行的线程”的面试干货。通过一次Java面试经历,我明白了停止线程不仅仅是技术问题,更是设计问题。Thread.stop()已被弃用,推荐使用Thread.interrupt()、标志位或ExecutorService来优雅地停止线程,避免资源泄漏和数据不一致。希望这篇文章能帮助你更好地理解Java多线程机制,面试顺利! 我是小米,喜欢分享技术的29岁程序员。欢迎关注我的微信公众号“软件求生”,获取更多技术干货!
118 53
|
2月前
|
数据采集 Java Linux
面试大神教你:如何巧妙回答线程优先级这个经典考题?
大家好,我是小米。本文通过故事讲解Java面试中常见的线程优先级问题。小明和小华的故事帮助理解线程优先级:高优先级线程更可能被调度执行,但并非越高越好。实际开发需权衡业务需求,合理设置优先级。掌握线程优先级不仅能写出高效代码,还能在面试中脱颖而出。最后,小张因深入分析成功拿下Offer。希望这篇文章能助你在面试中游刃有余!
53 4
面试大神教你:如何巧妙回答线程优先级这个经典考题?
|
2月前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
168 14
|
2月前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
72 13
|
2月前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
115 6
|
3月前
|
安全 Java 程序员
面试直击:并发编程三要素+线程安全全攻略!
并发编程三要素为原子性、可见性和有序性,确保多线程操作的一致性和安全性。Java 中通过 `synchronized`、`Lock`、`volatile`、原子类和线程安全集合等机制保障线程安全。掌握这些概念和工具,能有效解决并发问题,编写高效稳定的多线程程序。
121 11
|
3月前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
4月前
|
并行计算 算法 安全
面试必问的多线程优化技巧与实战
多线程编程是现代软件开发中不可或缺的一部分,特别是在处理高并发场景和优化程序性能时。作为Java开发者,掌握多线程优化技巧不仅能够提升程序的执行效率,还能在面试中脱颖而出。本文将从多线程基础、线程与进程的区别、多线程的优势出发,深入探讨如何避免死锁与竞态条件、线程间的通信机制、线程池的使用优势、线程优化算法与数据结构的选择,以及硬件加速技术。通过多个Java示例,我们将揭示这些技术的底层原理与实现方法。
198 3
|
5月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!

热门文章

最新文章