多线程面试题目(1)

发布时间:2024年01月12日

多线程基础

什么是多线程?多线程的优点与缺点?

多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务。

优点:可以提高 CPU 的利用率。在多线程程序中,一个线程必须等待的时候,CPU 可以运行其它的线程而不是等待,这样就大大提高了程序的效率。也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

缺点

  • 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多
  • 多线程需要协调和管理,所以需要CPU时间跟踪线程
  • 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题

创建线程的四种方式

继承Thread类,java是单继承
  • 定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体;
  • 创建Thread子类的实例,也就是创建了线程对象
  • 启动线程,调用线程的start()方法
public class CreateThread extends Thread{
    @Override
    public void run() {
        //获取线程名
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        CreateThread createThread = new CreateThread();
        //线程启动
        createThread.start();
    }
}
实现Runnable接口
  • 定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体。
  • 创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象。
  • 调用线程对象的start()方法来启动线程。
public class RunnableCreateThread implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnable接口创建线程");
    }

    public static void main(String[] args) {
        new Thread(new RunnableCreateThread()).start();
    }
}

匿名内部类:

new Thread(new Runnable() {
    @Override
    public void run() {
 		// 调用资源方法,完成业务逻辑
    }
}, "your thread name").start();

lambda表达式:

new Thread(() -> {

}, "your thread name").start();
  • 实现Callable接口
  • 使用Executor工具类创建线程池
使用Callable和Future创建线程

与 Runnable 接口不一样,Callable 接口提供了一个 call() 方法作为线程执行体,call() 方法比 run() 方法功能要强大,比如:call() 方法可以有返回值、call() 方法可以声明抛出异常。

  • 创建实现 Callable 接口的类 CreateThread1;
  • 以 createThread1为参数创建 FutureTask 对象;
  • 将 FutureTask 作为参数创建 Thread 对象
  • 调用线程对象的 start() 方法
public class CreateThread1 implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName());
        return "call() 方法可以有返回值、call() 方法可以声明抛出异常";
    }

    public static void main(String[] args) {
        //创建 FutureTask 对象
        FutureTask futureTask = new FutureTask<>(new CreateThread1());
        //创建线程并启动
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            Thread.sleep(1000);
            //获取返回值
            System.out.println("返回的结果是:" + futureTask.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
基于线程池创建线程

(可去查看线程池的章节)

public class CreateThreadByExecutors implements Runnable {
    
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        CreateThreadByExecutors thread = new CreateThreadByExecutors();
        for (int i = 0; i < 10; i++) {
            service.execute(thread);
            System.out.println("=======任务开始=========");
            service.shutdown();
        }
    }

    @Override
    public void run() {
        System.out.println("hello Thread");
    }
}

Runnablecallable有什么区别

相同点:

  • 两个都是接口。
  • 都是编写多线程程序。
  • 都是采用Thread.start()启动程序。

区别:

  • Runnable的**run()方法无返回值,callable的call()**方法提供返回值用来表示任务运行的结果。
  • Runnable提供**run()方法,无法通过throws抛出异常,异常必须在run()方法内部处理。callable提供call()**方法,直接抛出异常。

线程的状态有哪些

线程5个状态:创建、就绪、运行、阻塞和死亡。

  • 新建(new):新创建了一个线程对象。
  • 就绪(runnable):线程对象创建后,当调用线程对象的start()方法,该线程可能处于运行状态,可能处于就绪状态,等待被线程调度选中,获取cpu使用权。
  • 运行(running):线程获取CPU权限执行
  • 阻塞(blocked):阻塞包含三种情况:等待阻塞,同步阻塞,其他阻塞
    • 等待阻塞:wait(),进入等待队列,需要notify唤醒(但notify唤醒的线程是随机的)
    • 同步阻塞:获取同步锁失败进入阻塞
    • 其他阻塞:sleep(),join(),发出IO请求
  • 等待(WAITING)
  • 定时等待(Timed Waiting)
  • 消亡(terminated):线程执行结束

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

  1. 每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,run()方法称为线程体。通过调用Thread类的start()方法来启动一个线程。
  2. start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start()只能调用一次。
  3. start()方法来启动一个线程,真正实现了多线程运行。调用start()方法无需等待run方法体代码执行完毕,可以直接继续执行其他的代码; 此时线程是处于就绪状态,并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态, run()方法运行结束, 此线程终止。然后CPU再调度其它线程。
  4. run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接待用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法。

为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用run() 方法?

  1. new 一个线程Thread,线程进入新建状态;调用start(),会启动一个线程并使线程进入就绪状态,当分配到时间片就可以开始工作了,start()会执行线程的相应准备工作,然后自动执行run()方法的内容,这是真正的多线程工作。
  2. 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。

调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。

文章来源:https://blog.csdn.net/m0_37779600/article/details/135563249
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。