网教网

搜索
查看: 107|回复: 0

Java多线程面试题编程-面试官最喜欢听得解释

[复制链接]

2

主题

3

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2023-4-10 20:12:21 | 显示全部楼层 |阅读模式
作为面试官,肯定喜欢听面试者用自己的话来解释面试题,因为这可以展示面试者对于这个概念的理解程度以及能否清晰地表达技术概念,今天小岳主要给大家带来的是:什么是Java多线程、如何创建线程、线程都有哪些状态以及线程的生命周期和线程安全这几道面试题。接下来就请大家跟我一起来看看这几道java多线程面试题吧,当然也会给大家附上简单易懂的解释哦!




1.  什么是多线程?



多线程是一种编程技术,可以让计算机同时处理多个任务,这些任务可以在同一时间内运行,提高了程序的效率。类比于日常生活,就像一个人同时可以做多件事情,比如说同时听音乐、看视频、写作业等等,毕竟我管不住自己的时候确实会这样干,哈哈!有没有好奇的孩子想听听我是如何同时听音乐、看视频、写作业的呢?只要你们想知道,我都会偷偷告诉你们哦!




举个例子,比如你在下载一个很大的文件,如果你使用单线程下载,那么你只能等待文件下载完成后才能去做其他事情。但是如果你使用多线程下载,那么你可以同时开启多个线程下载这个文件的不同部分,这样可以加快下载速度,同时也可以让你在下载的同时做其他事情,提高了效率。
贴合生活中的例子来跟大家做解释,是不是发现多线程的概念也没有那么难懂,所以记住一句话,代码是死的,人是活得,是代码服务我们,是这些技术来优化我们的生活,所以一定要活学活用!

2.  Java中如何创建线程?














public class CookThread extends Thread {
private String task;

public CookThread(String task) {
this.task = task;
}

public void run() {
if (task.equals("boilWater")) {
boilWater();
} else if (task.equals("cookNoodle")) {
cookNoodle();
}
}

public void boilWater() {
System.out.println("开始烧开水...");
// 烧开水的逻辑
System.out.println("烧开水完成!");
}

public void cookNoodle() {
System.out.println("开始煮面条...");
// 煮面条的逻辑
System.out.println("煮面条完成!");
}
}




CookThread boilWaterThread = new CookThread("boilWater");
CookThread cookNoodleThread = new CookThread("cookNoodle");
boilWaterThread.start();
cookNoodleThread.start();



public class CookTask implements Runnable {
private String task;

public CookTask(String task) {
this.task = task;
}

public void run() {
if (task.equals("boilWater")) {
boilWater();
} else if (task.equals("cookNoodle")) {
cookNoodle();
}
}

public void boilWater() {
System.out.println("开始烧开水...");
// 烧开水的逻辑


以上就是针对如何创建线程做出的简单汇总,大家可以继续练习了哦!

3.  线程有哪些状态?







示例代码:
Thread thread = new Thread();


示例代码:
Thread thread = new Thread(() -> {
System.out.println("Hello, world!");
});
thread.start(); // 线程进入可运行状态


示例代码:
Object lock = new Object();

Thread thread1 = new Thread(() -> {
    synchronized (lock) {
        // 执行同步代码块
    }
});

Thread thread2 = new Thread(() -> {
    synchronized (lock) {
        // 执行同步代码块
    }
});

thread1.start(); // 线程1进入可运行状态
thread2.start(); // 线程2进入阻塞状态,因为锁被线程1持有


示例代码:
Object lock = new Object();

Thread thread1 = new Thread(() -> {
    synchronized (lock) {
        try {
            lock.wait(); // 线程1进入等待状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

Thread thread2 = new Thread(() -> {
    synchronized (lock) {
        lock.notify(); // 唤醒线程1
    }
});

thread1.start(); // 线程1进入等待状态
thread2.start(); // 唤醒线程1,使其进入可运行状态


示例代码:
Thread thread = new Thread(() -> {
    try {
        Thread.sleep(1000); // 线程进入计时等待状态
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

thread.start(); // 线程进入可运行状态


以上就是线程的运行状态做出的简单总结,大家可以继续学习了哦!

4.  线程的生命周期是怎样的?


线程的生命周期包括五个不同的阶段:新建、就绪、运行、阻塞和死亡。下面小岳会结合代码案例来分析线程的生命周期。




代码示例一:


public class ThreadStateExample implements Runnable {

@Override
public void run() {
try {
// 线程运行5秒钟
Thread.sleep(5000);
} catch (InterruptedException e) {
            e.printStackTrace();
}
}

public static void main(String[] args) {
Thread thread = new Thread(new ThreadStateExample());

// 线程新建状态
System.out.println("Thread state: " + thread.getState());

// 启动线程
        thread.start();

// 线程就绪状态
System.out.println("Thread state: " + thread.getState());

// 线程运行状态
while (thread.isAlive()) {
System.out.println("Thread state: " + thread.getState());
}

// 线程死亡状态
System.out.println("Thread state: " + thread.getState());
}
}







当线程处于阻塞状态时,可以使用以下方法之一使其恢复。
● sleep(long millis): 使当前线程休眠指定的毫秒数。
● wait(): 当前线程等待另一个线程调用notify()或notifyAll()方法。
● join(): 等待线程执行完成。
● park(): 阻塞当前线程,直到调用unpark(Thread thread)方法。



代码示例二:



假设我们要开发一个简单的多线程程序,用来计算1到1000之间的所有偶数的和。我们可以创建一个名为"EvenSumThread"的线程类,继承自Thread类,并实现run()方法,用来执行线程的任务。具体代码如下:
public class EvenSumThread extends Thread {
private int sum;

public void run() {
for (int i = 2; i <= 1000; i += 2) {
            sum += i;
System.out.println("Thread " + Thread.currentThread().getId() + " is running, sum is " + sum);
}
}

public int getSum() {
return sum;
}
}在这个程序中,我们在EvenSumThread类中定义了一个私有变量sum,用来记录1到1000之间所有偶数的和。在run()方法中,我们使用for循环遍历1到1000之间的所有偶数,并将它们相加,同时在控制台上输出线程的ID和当前的sum值。在getSum()方法中,我们可以获取计算结果。
现在,我们来分析一下这个多线程程序中线程的生命周期:




新建阶段(New):当我们创建EvenSumThread对象时,线程进入了新建阶段,此时还没有开始执行线程任务。
EvenSumThread evenSumThread = new EvenSumThread();


就绪阶段(Runnable):当我们调用evenSumThread.start()方法时,线程进入了就绪阶段,此时线程已经准备好执行,但是还没有得到CPU的时间片。
evenSumThread.start();


运行阶段(Running):当线程获得了CPU时间片并开始执行run()方法时,线程进入了运行阶段。




阻塞阶段(Blocked):在本例中,线程不会进入阻塞阶段,因为它没有被阻塞。




死亡阶段(Terminated):当线程完成了它的任务或者抛出了未捕获的异常时,线程进入了终止阶段。
System.out.println("The sum of even numbers from 1 to 1000 is " + evenSumThread.getSum());以上就是一个多线程程序中线程的生命周期,从新建、就绪、运行、阻塞到终止,每个阶段都有不同的特点和表现。


5.  什么是线程安全?



线程安全指多个线程同时访问同一个共享资源时,不会产生不一致的结果或者出现异常的情况。Java提供了多种机制来实现线程安全,例如使用同步代码块、同步方法、使用线程安全的集合等。下面给出一个生活案例,通俗易懂的跟大家解释一番究竟什么是线程安全?如图所示:





public class Counter {
private int count;

public synchronized void increment() {
        count++;
}

public synchronized void decrement() {
        count--;
}

public synchronized int getCount() {
return count;
}
}在这个示例中,Counter类维护一个计数器变量count,提供了三个同步方法:increment()、decrement()和getCount()。这些方法都使用了synchronized关键字,因此它们在任何时候都只能由一个线程访问。这样,即使有多个线程同时访问Counter对象,也不会出现线程安全问题。
需要注意的是,使用synchronized关键字会降低程序的执行效率,因为每次访问同步方法或同步代码块都需要获取锁,这会导致线程的阻塞。因此,在实现线程安全的同时,还应该考虑性能问题。
以上就是关于java线程安全的简单介绍,大家一定不要死记硬背面试题哦,可以结合生活案例以及代码来分析理解它,这样才能有效加深印象哦!


6.  总结


好啦!以上就是针对部分面试题来给大家做出的简单介绍,当然都是比较结合生活和通俗易懂的介绍,希望可以对大家的面试带来帮助哦!


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表