多线程相关面试题

简介: 简介: 哈喽,大家好~我是你们的老朋友:保护小周ღ,本期为大家带来的是多线程相关面试题, 简述 synchronized 和 ReentrantLock 的区别,wait() 和 sleep() 的区别volatile 关键字的作用,线程池的执行流程和拒绝策略,Callable 带返回值的任务更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘

一、简述 synchronized 和 ReentrantLock 的区别

  1. 使用方法不同

Synchronized 可以用来修饰普通方法,静态代码块和普通代码块

ReentrantLock 只能在普通代码块上使用

  1. 获取锁和释放锁的方式不同

Synchronized 会自动的加锁和释放锁,当进入synchronized 修饰的代码块之后会自动的加锁,当离开代码块的时候会自动的释放锁

ReentrantLock 需要手动的加锁和释放锁

lock() 方法加锁, unlock() 方法释放锁

unlock() 释放锁一定要放在 finally中,否则有可能会出现锁一直被占用,从而导致其他线程一直阻塞等待的

  1. Synchronized 是非公平锁,而 ReentrantLock 既可以是公平锁,也可以是非公平锁,默认是非公平锁,也可以手动指定为公平锁
  2. 中断响应不同

ReentrantLock 可以使用 lockInterruptibly 获取锁并响应中断指令,而 synchronized 不能响应中断,如果发生了死锁就会一直等待下去,而使用 ReentrantLock 可以响应中断并释放锁,从而解决死锁的问题。

  1. 底层实现不同

synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的

AQS,全称是 AbstractQueuedSynchronizer,中文译为抽象队列式同步器。这个抽象类对于JUC并发包非常重要,JUC包中的ReentrantLock,,Semaphore,ReentrantReadWriteLock,CountDownLatch 等等几乎所有的类都是基于AQS实现的。


二、volatile 关键字的作用

  1. volatile 是java 中的关键字,是一个变量修饰符,常常被用来修饰需要被不同线程访问和修改的变量,
  2. 被 volatile 修饰的变量具有可见性,当一个线程修改了该变量的值时,其他线程如果对该变量进行操作的时候会从内存中重新读取该变量的数据,可以保障了数据的有效性,避免内存可见性造成的线程安全问题(bug)
  3. volatile 只能保证单次读/写操作的原子性(针对一条指令),不能保证多步操作的原子性。例如:修改一个变量的值:第一步将数据从内存中读取到寄存器中,cpu 从寄存器中读取数据,对数据进行处理后重新写入到内存,我们可以把三步操作看作是修改变量的一个事件,使用 volatile 不能保证该事件的原子性,可能执行第一步的时候cpu 就执行了别的线程,如果别的线程也对同一变量进行修改,就会造成bug , 针对这个问题我们需要使用 synchrnized 对事件进行加锁,即可保证事件的原子性,此时其他线程如果也需要对同一变量进行操作,只能阻塞等待当前线程将事件处理完毕。
  4. 在 Java 内存模型中,允许编译器和处理器对指令进行重排序(最优的处理效率),重排序过程不会影响到单线程程序的执行结果,但是可能会影响到多线程并发执行的正确性。volatile 修饰的变量的读写指令不能和其前后的任何指令重排序,其前后的指令可能会被重排序, volatile 关键字可以禁止指令重排序,

三、wait()  和 sleep() 的区别

  1. wait是Object类中的一个方法,sleep是Thread类中的一个方法;
  2. wait必须在synchronized修饰的代码块或方法中使用,sleep方法可以在任何位置使用;
  3. wait被调用后当前线程进入BLOCK状态并释放当前对象锁,并可以通过notify和notifyAll方法进行唤醒;sleep被调用后当前线程进入TIMED_WAIT状态,不涉及锁相关的操作;

四、线程池的执行流程和拒绝策略

线程池的执行流程有 3 个重要的判断点:

判断当前工作的线程数是否小于核心线程数

判断当前任务队列是否已满.

判断当前线程数是否已达最大线程数.

如果在经过上述三个过程后, 得到的结果都是 true , 那么就会执行线程池的拒绝策略.


拒绝策略有四种,是封装在 ThreadPoolExecutor类中的静态方法

AbortPolicy:中止策略,线程池会抛出RejectedRxecutionException异常并中止执行此任务.

CallerRunsPolicy:把任务交给添加此任务的(main)线程来执行.

DiscardPolicy:忽略此任务,忽略最新的一个任务.

DiscardOldestPolicy:忽略最早的任务,最先加入队列的任务


五、Callable 带返回值的任务

创建一个匿名内部类,实现 Callable接口. Callable带有泛型参数.泛型参数表示返回值的类型.

重写 Callable的 call()方法,完成累加的过程.直接通过返回值返回计算结果.

把 callable实例使用 FutureTask包装一下.

创建线程,线程的构造方法传入 FutureTask .此时新线程就会执行 FutureTask内部的

Callable的call方法,完成计算.计算结果就放到了 FutureTask对象中.

在主线程中调用 futureTask.get()能够阻塞等待新线程计算完毕.并获取到 FutureTask中的结果.

可以看到,使用 Callable和 FutureTask之后,代码简化了很多,也不必手动写线程同步代码了.

Callable<Integer>callable=newCallable<Integer>() {
@OverridepublicIntegercall() throwsException {
intsum=0;
for (inti=1; i<=1000; i++) {
sum+=i;
      }
returnsum;
  }
};
FutureTask<Integer>futureTask=newFutureTask<>(callable);
Threadt=newThread(futureTask);
t.start();
intresult=futureTask.get();
System.out.println(result);

Callable和 Runnable相对,都是描述一个 “任务”. Callable描述的是带有返回值的任务,Runnable描述的是不带返回值的任务.Callable通常需要搭配 FutureTask来使用. FutureTask用来保存 Callable的返回结果.因为Callable往往是在另一个线程中执行的,啥时候执行完并不确定.FutureTask就可以负责这个等待结果出来的工作.

理解 FutureTask

想象去吃麻辣烫.当餐点好后,后厨就开始做了.同时前台会给你一张 “小票” .这个小票就是FutureTask.后面我们可以随时凭这张小票去查看自己的这份麻辣烫做出来了没.

相关面试题

介绍下 Callable是什么

Callable是一个 interface .相当于把线程封装了一个 “返回值”.方便程序猿借助多线程的方式计算结果.

Callable和 Runnable相对,都是描述一个 “任务”. Callable描述的是带有返回值的任务,Runnable描述的是不带返回值的任务.

Callable通常需要搭配 FutureTask来使用. FutureTask用来保存 Callable的返回结果.因为Callable往往是在另一个线程中执行的,啥时候执行完并不确定.



相关文章
|
1月前
|
并行计算 安全 Java
C# .NET面试系列四:多线程
<h2>多线程 #### 1. 根据线程安全的相关知识,分析以下代码,当调用 test 方法时 i > 10 时是否会引起死锁? 并简要说明理由。 ```c# public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } ``` 在给定的代码中,不会发生死锁。死锁通常是由于两个或多个线程互相等待对方释放锁而无法继续执行的情况。在这个代码中,只有一个线程持有锁,且没有其他线程参与,因此不
105 3
|
3月前
|
Java 调度 Windows
JAVA面试八股文之多线程基础知识
JAVA面试八股文之多线程基础知识
|
4月前
|
Java 关系型数据库 数据库连接
BATJ高频面试249道题:微服务+多线程+分布式+MyBatis +Spring
本文收集整理了各大厂常见面试题N道,你想要的这里都有内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、Spring Boot、Spring Cloud、RabbitMQ、Kafka、Linux 等技术栈,希望大家都能找到适合自己的公司,开开心心的撸代码。
|
10天前
|
调度 Python
Python多线程、多进程与协程面试题解析
【4月更文挑战第14天】Python并发编程涉及多线程、多进程和协程。面试中,对这些概念的理解和应用是评估候选人的重要标准。本文介绍了它们的基础知识、常见问题和应对策略。多线程在同一进程中并发执行,多进程通过进程间通信实现并发,协程则使用`asyncio`进行轻量级线程控制。面试常遇到的问题包括并发并行混淆、GIL影响多线程性能、进程间通信不当和协程异步IO理解不清。要掌握并发模型,需明确其适用场景,理解GIL、进程间通信和协程调度机制。
28 0
|
1月前
|
消息中间件 存储 算法
【C/C++ 泡沫精选面试题04】在实际项目中,多进程和多线程如何选择?
【C/C++ 泡沫精选面试题04】在实际项目中,多进程和多线程如何选择?
43 1
|
7月前
|
安全
多线程访问同步方法的7种情况(面试常考)
多线程访问同步方法的7种情况(面试常考)
35 0
多线程访问同步方法的7种情况(面试常考)
|
3月前
|
消息中间件 数据库
面试题解析:RabbitMQ在多线程秒杀系统中的关键作用
面试题解析:RabbitMQ在多线程秒杀系统中的关键作用
34 0
|
3月前
|
存储 算法 安全
程序员的100大Java多线程面试问题及答案(二)
程序员的100大Java多线程面试问题及答案(二)
|
3月前
|
存储 安全 Java
程序员的100大Java多线程面试问题及答案(一)
程序员的100大Java多线程面试问题及答案(一)
|
3月前
|
安全 Linux Go
golang面试:golang并发与多线程(三)
golang面试:golang并发与多线程(三)
45 0