面试官又问多线程

简介: 面试官又问多线程

一.创建线程方式

面试官:先自我介绍一下。

小面:哔哩吧啦。。。。。

面试官:先介绍一下创建线程的方法吧。

小面:心想"好彩八股文背过"

创建线程分为两种:

  • 不需要获取结果,不需要获取到线程的执行结果的。
  • 通过继承Thread,重写run方法实现
  • 通过实现Runnable接口,需要通过Thread来构造,调用start方法启动
public static void main(String[] args) {
        SubThread subThread = new SubThread();
        subThread.start();
        Thread thread = new Thread(new SubThread2());
        thread.start();
}
//继承Thread方式
private static class SubThread extends Thread{
        @Override
        public void run() {
            System.out.println("我是小面");
        }
}
//实现Runnable方式
private static class SubThread2 implements Runnable{
        @Override
        public void run() {
            System.out.println("我是小面2");
        }
}
  • 需要获取结果,有时候我们需要获取到线程的执行结果
通过实现Callable,重写call方法,callable是泛型类,具体类型是返回的结果类型。结果需要借助FutureTask来获取,FutureTask需要放到Thread来启动。另外FutureTask是实现了Future,所以支持cancel,isDone,get这些方法。
public static void main(String[] args) {
        FutureTask<String> futureTask = new FutureTask<>(new SubThread3());
        Thread thread2 = new Thread(futureTask);
        thread2.start();
        try {
            //获取到结果输出
            System.out.println( futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
}
private static class SubThread3 implements Callable<String>{
        @Override
        public String call() throws Exception {
            return "我是小面3";
        }
}


二.等待结果

面试官:不错,那创建了子线程后,主线程怎么等待子线程完成。

比如泡茶的例子,分成三个线程,主线程需要等待两个子线程完成后才能进行,因为需要依赖子线程的结果。

  • 主线程:泡茶,喝茶
  • 子线程1:洗茶具
  • 子线程2:烧水

小面:这个问题之前学习并发编程课程的时候也学习过。

最简单的方式:join方法

在主线程调用子线程1和子线程2的join方法,主线程就会阻塞,一直等到子线程1和子线程2完成后才会运行。

public static void main(String[] args) {
        SubThread subThread = new SubThread();
        subThread.start();
        SubThread subThread2 = new SubThread();
        subThread2.start();
        try {
            subThread.join();
            subThread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "主线程开始泡茶");
}
private static class SubThread extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":开始洗茶具");
            try {
                //模拟洗茶具
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":开始洗茶具结束");
        }
}
private static class SubThread2 extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":烧水开始");
            try {
                //模拟烧水
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":烧水结束");
        }
}


获取到返回结果:FutureTask

join方法不能获取到子线程的执行结果,需要获取到返回结果可以通过FutureTask+Callable方式实现

public static void main(String[] args) {
        FutureTask<Boolean> futureTask1 = new FutureTask<>(new SubCallable());
        FutureTask<Boolean> futureTask2 = new FutureTask<>(new SubCallable2());
        Thread thread1 = new Thread(futureTask1);
        thread1.start();
        Thread thread2 = new Thread(futureTask2);
        thread2.start();
        while(true){
            try {
                if (futureTask1.get() && futureTask2.get()) {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "主线程开始泡茶");
}
private static class SubCallable implements Callable<Boolean> {
        @Override
        public Boolean call() throws Exception {
            System.out.println(Thread.currentThread().getName() + ":开始洗茶具");
            try {
                //模拟洗茶具
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                return false;
            }
            System.out.println(Thread.currentThread().getName() + ":开始洗茶具结束");
            return true;
        }
}
private static class SubCallable2 implements Callable<Boolean>  {
        @Override
        public Boolean call() throws Exception {
            System.out.println(Thread.currentThread().getName() + ":烧水开始");
            try {
                //模拟烧水
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                return false;
            }
            System.out.println(Thread.currentThread().getName() + ":烧水结束");
            return true;
        }
}


非阻塞方式获取到结果

通过FutureTask只能通过阻塞的方式获取结果,这时候主线程就什么也不能做,有没有一种方式可以不阻塞主线程,获取结果呢?

通过Guava异步任务接口:基于回调的方式实现,需要配合线程池使用。

把线程池放到ListeningExecutorService里,调用submit方法提交callable任务,会返回listenableFuture

Futures.addCallback把listenableFuture和回调函数绑定起来,子线程完成后就会调用回调函数,这样就不需要阻塞主线程了。

需要引入依赖:
<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
</dependency>
public static void main(String[] args) throws InterruptedException {
        final boolean[] subThreadDone = {false, false};
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);
        ListenableFuture<Boolean> listenableFuture1 = listeningExecutorService.submit(new SubCallable());
        ListenableFuture<Boolean> listenableFuture2 = listeningExecutorService.submit(new SubCallable2());
        Futures.addCallback(listenableFuture1, new FutureCallback<Boolean>() {
            @Override
            public void onSuccess(@Nullable Boolean aBoolean) {
                subThreadDone[0] = aBoolean;
            }
            @Override
            public void onFailure(Throwable throwable) {
                subThreadDone[0] = false;
            }
        }, executorService);
        Futures.addCallback(listenableFuture2, new FutureCallback<Boolean>() {
            @Override
            public void onSuccess(@Nullable Boolean aBoolean) {
                subThreadDone[1] = aBoolean;
            }
            @Override
            public void onFailure(Throwable throwable) {
                subThreadDone[1] = false;
            }
        }, executorService);
        while (true) {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + ":看书");
            if (subThreadDone[0] && subThreadDone[1]) {
                System.out.println(Thread.currentThread().getName() + "主线程开始泡茶");
            }
        }
}
private static class SubCallable implements Callable<Boolean> {
        @Override
        public Boolean call() throws Exception {
            System.out.println(Thread.currentThread().getName() + ":开始洗茶具");
            try {
                //模拟洗茶具
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                return false;
            }
            System.out.println(Thread.currentThread().getName() + ":开始洗茶具结束");
            return true;
        }
}
private static class SubCallable2 implements Callable<Boolean> {
        @Override
        public Boolean call() throws Exception {
            System.out.println(Thread.currentThread().getName() + ":烧水开始");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                return false;
            }
            System.out.println(Thread.currentThread().getName() + ":烧水结束");
            return true;
        }
}

小结

面试官听完小面的回答,点了点头,说你在这里等一下。想知道小面有没有通过面试,可以持续关注后续文章。

总结一下:

创建线程时:

  1. 不需要获取结果可以使用Runnable和Thread创建
  2. 需要获取结果可以使用Callable+FutureTask搭配使用

等待结果时:

  • 使用调用线程的join方法,就会一直等待该线程完成,才往下运行
  • 通过FutureTask+Callable方式,调用FutureTask的get方法获取结果
  • 通过guava工具包,创建listenableFuture回调的方式获取结果,这种方式可以不阻塞线程

你学会了吗?欢迎持续关注!

相关文章
|
2月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
2月前
|
消息中间件 前端开发 NoSQL
面试官:线程池遇到未处理的异常会崩溃吗?
面试官:线程池遇到未处理的异常会崩溃吗?
75 3
面试官:线程池遇到未处理的异常会崩溃吗?
|
2月前
|
消息中间件 存储 前端开发
面试官:说说停止线程池的执行流程?
面试官:说说停止线程池的执行流程?
51 2
面试官:说说停止线程池的执行流程?
|
2月前
|
消息中间件 前端开发 NoSQL
面试官:如何实现线程池任务编排?
面试官:如何实现线程池任务编排?
33 1
面试官:如何实现线程池任务编排?
|
3月前
|
Java
【多线程面试题二十五】、说说你对AQS的理解
这篇文章阐述了对Java中的AbstractQueuedSynchronizer(AQS)的理解,AQS是一个用于构建锁和其他同步组件的框架,它通过维护同步状态和FIFO等待队列,以及线程的阻塞与唤醒机制,来实现同步器的高效管理,并且可以通过实现特定的方法来自定义同步组件的行为。
【多线程面试题二十五】、说说你对AQS的理解
|
3月前
|
Java
【多线程面试题十六】、谈谈ReentrantLock的实现原理
这篇文章解释了`ReentrantLock`的实现原理,它基于Java中的`AbstractQueuedSynchronizer`(AQS)构建,通过重写AQS的`tryAcquire`和`tryRelease`方法来实现锁的获取与释放,并详细描述了AQS内部的同步队列和条件队列以及独占模式的工作原理。
【多线程面试题十六】、谈谈ReentrantLock的实现原理
|
3月前
|
消息中间件 缓存 算法
Java多线程面试题总结(上)
进程和线程是操作系统管理程序执行的基本单位,二者有明显区别: 1. **定义与基本单位**:进程是资源分配的基本单位,拥有独立的内存空间;线程是调度和执行的基本单位,共享所属进程的资源。 2. **独立性与资源共享**:进程间相互独立,通信需显式机制;线程共享进程资源,通信更直接快捷。 3. **管理与调度**:进程管理复杂,线程管理更灵活。 4. **并发与并行**:进程并发执行,提高资源利用率;线程不仅并发还能并行执行,提升执行效率。 5. **健壮性**:进程更健壮,一个进程崩溃不影响其他进程;线程崩溃可能导致整个进程崩溃。
48 2
|
3月前
|
存储 安全 容器
【多线程面试题二十一】、 分段锁是怎么实现的?
这篇文章解释了分段锁的概念和实现方式,通过将数据分成多个段并在每段数据上使用独立锁,从而降低锁竞争,提高并发访问效率,举例说明了`ConcurrentHashMap`如何使用分段锁技术来实现高并发和线程安全。
【多线程面试题二十一】、 分段锁是怎么实现的?
|
3月前
|
安全 Java
【多线程面试题十九】、 公平锁与非公平锁是怎么实现的?
这篇文章解释了Java中`ReentrantLock`的公平锁和非公平锁的实现原理,其中公平锁通过检查等待队列严格按顺序获取锁,而非公平锁允许新线程有更高机会立即获取锁,两者都依赖于`AbstractQueuedSynchronizer`(AQS)和`volatile`关键字以及CAS技术来确保线程安全和锁的正确同步。
【多线程面试题十九】、 公平锁与非公平锁是怎么实现的?
|
3月前
|
存储 缓存 安全
Java多线程面试题总结(中)
Java内存模型(JMM)定义了程序中所有变量的访问规则与范围,确保多线程环境下的数据一致性。JMM包含主内存与工作内存的概念,通过8种操作管理两者间的交互,确保原子性、可见性和有序性。`synchronized`和`volatile`关键字提供同步机制,前者确保互斥访问,后者保证变量更新的可见性。多线程操作涉及不同状态,如新建(NEW)、可运行(RUNNABLE)等,并可通过中断、等待和通知等机制协调线程活动。`volatile`虽不确保线程安全,但能确保变量更新对所有线程可见。
19 0