• 【建议收藏】对线面试官:多线程硬核50问


    前言

    金九银十快要来了,整理了50道多线程并发面试题,大家可以点赞、收藏起来,慢慢品!~

    1、为什么要使用多线程

    选择多线程的原因,就是因为快。举个例子:

    如果要把1000块砖搬到楼顶,假设到楼顶有几个电梯,你觉得用一个电梯搬运快,还是同时用几个电梯同时搬运快呢?这个电梯就可以理解为线程。

    所以,我们使用多线程就是因为:在正确的场景下,设置恰当数目的线程,可以用来程提高序的运行速率。更专业点讲,就是充分地利用CPU和I/O的利用率,提升程序运行速率。

    当然,有利就有弊,多线程场景下,我们要保证线程安全,就需要考虑加锁。加锁如果不恰当,就很很耗性能。

    2. 创建线程有几种方式?

    Java中创建线程主要有以下这几种方式:

    • 定义Thread类的子类,并重写该类的run方法
    • 定义Runnable接口的实现类,并重写该接口的run()方法
    • 定义Callable接口的实现类,并重写该接口的call()方法,一般配合Future使用
    • 线程池的方式

    2.1 定义Thread类的子类,并重写该类的run方法

    public class ThreadTest {
    
        public static void main(String[] args) {
            Thread thread = new MyThread();
            thread.start();
        }
    }
    
    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("111");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2 定义Runnable接口的实现类,并重写该接口的run()方法

    public class ThreadTest {
    
        public static void main(String[] args) {
            MyRunnable myRunnable = new MyRunnable();
            Thread thread = new Thread(myRunnable);
            thread.start();
        }
    }
    
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("111");
        }
    }
    //运行结果:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.3 定义Callable接口的实现类,并重写该接口的call()方法

    如果想要执行的线程有返回,可以使用Callable。

    public class ThreadTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            MyThreadCallable mc = new MyThreadCallable();
            FutureTask ft = new FutureTask<>(mc);
            Thread thread = new Thread(ft);
            thread.start();
            System.out.println(ft.get());
        }
    }
    
    class MyThreadCallable implements Callable {
        @Override
        public String call()throws Exception {
            return "111";
        }
    }
    //运行结果:
    111
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.4 线程池的方式

    日常开发中,我们一般都是用线程池的方式执行异步任务。

    public class ThreadTest {
    
        public static void main(String[] args) throws Exception {
    
            ThreadPoolExecutor executorOne = new ThreadPoolExecutor(5, 5, 1,
                    TimeUnit.MINUTES, new ArrayBlockingQueue(20), new CustomizableThreadFactory("Tianluo-Thread-pool"));
            executorOne.execute(() -> {
                System.out.println("111");
            });
    
            //关闭线程池
            executorOne.shutdown();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3. start()方法和run()方法的区别

    其实start和run的主要区别如下:

    • start方法可以启动一个新线程,run方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程。
    • start方法实现了多线程,而run方法没有实现多线程。
    • start不能被重复调用,而run方法可以。
    • start方法中的run代码可以不执行完,就继续执行下面的代码,也就是说进行了线程切换。然而,如果直接调用run方法,就必须等待其代码全部执行完才能继续执行下面的代码。

    大家可以结合代码例子来看看哈~

    public class ThreadTest {
    
        public static void main(String[] args){
            Thread t=new Thread(){
                public void run(){
                    pong();
                }
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    AI搜索,围攻百度
    pycharm 快捷键 及 高级设置
    LVS-Nat模式实战
    实践小记—静态成员的使用注意(或许由此产生的不知名Bug)
    数据结构——动态顺序表
    【论文阅读】MPViT : Multi-Path Vision Transformer for Dense Prediction
    Postman下发流表至Opendaylight
    JAVA毕业设计酒店员工管理系统计算机源码+lw文档+系统+调试部署+数据库
    JS(JavaScript)
    localStorage和sessionStorage用法与区别
  • 原文地址:https://blog.csdn.net/weixin_57907028/article/details/126816463