面试多线程八股文十问十答第一期

news2025/2/24 10:26:33

面试多线程八股文十问十答第一期

作者:程序员小白条,个人博客

相信看了本文后,对你的面试是有一定帮助的!

⭐点赞⭐收藏⭐不迷路!⭐

1.ThreadLocal如何实现线程安全

  • Java的ThreadLocal是一个线程本地变量,它提供了一种简单的机制来实现线程封闭(Thread confinement)。ThreadLocal为每个线程提供了一个独立的副本,每个线程都可以访问自己的副本,从而避免了线程安全问题。
  • ThreadLocal实现线程安全的核心是每个线程都有自己的变量副本,每个线程访问的都是自己的变量副本,从而避免了多线程之间的竞争和冲突。当多个线程共享同一个对象时,如果没有采取线程安全的措施,就会出现多个线程同时修改同一个对象的情况,从而导致数据不一致或者出现异常。
  • ThreadLocal的作用:实现线程范围内的局部变量,即ThreadLocal在一个线程中是共享的,在不同线程之间是隔离的。

在Java中,使用ThreadLocal实现线程安全的具体步骤如下:

  1. 在类中创建ThreadLocal变量,每个线程都可以访问这个变量的副本。
  2. 在需要访问共享变量的方法中,通过ThreadLocal.get()方法获取当前线程的变量副本。
  3. 如果当前线程没有创建过变量副本,那么就通过ThreadLocal.set()方法创建一个变量副本。
  4. 在方法执行结束时,通过ThreadLocal.remove()方法删除当前线程的变量副本。
  • 通过ThreadLocal实现线程安全的好处是可以简化线程安全的处理过程,避免了显式的同步操作,提高了代码的可读性和可维护性。但是需要注意的是,ThreadLocal只能保证线程内部的线程安全,无法保证多个线程之间的线程安全。在多线程环境下,仍然需要采取其他措施来保证多个线程之间的数据同步和一致性。
  • 也就是说,每个线程访问的是自己独立的变量副本,但是多个线程之间共享的数据还是存在竞争和冲突的可能性。如果需要保证多个线程之间的数据同步和一致性,仍然需要采取其他措施,例如使用synchronized关键字或者使用锁机制等。因此,需要根据实际的业务场景和需求来选择合适的线程安全措施,不能仅仅依靠ThreadLocal来实现线程安全。

2.子线程如何获得父线程的ThreadLocal

在Java中,子线程无法直接访问父线程的ThreadLocal变量,因为ThreadLocal是线程封闭的,每个线程都拥有自己独立的变量副本。

但是,如果在创建子线程时将父线程的ThreadLocal变量传递给子线程,子线程就可以访问父线程的ThreadLocal变量了。具体实现方法如下:

1.在父线程中创建并初始化ThreadLocal变量。

2.在创建子线程时,将父线程的ThreadLocal变量作为参数传递给子线程。

3.在子线程中通过参数获取父线程的ThreadLocal变量,并使用该变量。

public class ParentThread {
    // 创建父线程的ThreadLocal变量
    public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();

    public static void main(String[] args) {
        // 设置父线程的ThreadLocal变量
        threadLocal.set("Hello World!");

        // 创建子线程并将父线程的ThreadLocal变量传递给子线程
        Thread childThread = new Thread(new ChildThread(threadLocal.get()));
        childThread.start();
    }
}

class ChildThread implements Runnable {
    private String value;

    public ChildThread(String value) {
        this.value = value;
    }

    public void run() {
        // 在子线程中获取父线程的ThreadLocal变量
        String parentValue = this.value;
        System.out.println("Parent Thread Local Value: " + parentValue);
    }
}

3.线程能同时访问类中的两个Synchronized的同步方法吗?这两个同步方法能互相访问吗?

  • 线程可以同时访问类中的两个Synchronized的同步方法,但是这两个同步方法不能互相访问。
  • Synchronized关键字可以保证同一时间只有一个线程进入同步代码块或同步方法,如果一个线程进入了一个Synchronized方法或代码块,其他线程就必须等待该线程执行完毕才能进入该方法或代码块。
  • 但是,如果一个线程已经获得了一个Synchronized方法或代码块的锁,它就可以同时访问另一个Synchronized方法或代码块,因为这两个方法使用的是不同的锁。也就是说,如果类中有两个Synchronized方法,这两个方法使用的锁是不同的,因此不会相互影响,线程可以同时访问它们。
  • 然而,如果一个Synchronized方法或代码块在执行过程中调用了另一个Synchronized方法或代码块,就会出现锁竞争(在锁一样的情况下)的问题,即第一个Synchronized方法或代码块已经获得了锁,但是第二个Synchronized方法或代码块也需要获得同样的锁才能执行,因此第二个Synchronized方法或代码块就会被阻塞。如果第一个Synchronized方法或代码块等待第二个Synchronized方法或代码块执行完毕才能继续执行,就会出现死锁的情况。因此,两个Synchronized方法或代码块不能互相访问,否则会出现锁竞争和死锁问题。

4.sleep()和wait()方法的作用

线程sleep 和wait 的区别:

1、这两个方法来自不同的类分别是Thread和Object

2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)

4、sleep必须捕获异常,wait必须捕获异常(之前看到网上很多博客都说wait方法不需要抛出异常这个观点是错误的,千万不要被误导了!!!!!!!notify和notifyAll方法确实可以不用抛出异常)

不加抛异常处理的会编译通不过

sleep是Thread类的静态方法。sleep的作用是让线程休眠制定的时间,在时间到达时恢复,也就是说sleep将在接到时间到达事件事恢复线程执行。wait是Object的方法,也就是说可以对任意一个对象调用wait方法,调用wait方法将会将调用者的线程挂起,直到其他线程调用同一个对象的notify方法才会重新激活调用者。

共同点:

它们都可以被interrupted方法中断。

Thread.Sleep(1000) 意思是在未来的1000毫秒内本线程不参与CPU竞争,1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。另外值得一提的是Thread.Sleep(0)的作用,就是触发操作系统立刻重新进行一次CPU竞争,竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。

wait(1000)表示将锁释放1000毫秒,到时间后如果锁没有被其他线程占用,则再次得到锁,然后wait方法结束,执行后面的代码,如果锁被其他线程占用,则等待其他线程释放锁。注意,设置了超时时间的wait方法一旦过了超时时间,并不需要其他线程执行notify也能自动解除阻塞,但是如果没设置超时时间的wait方法必须等待其他线程执行notify。sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程中,线程不会释放对象锁。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

  • sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用。

5.Lock锁

  • synchronized是Java语言的关键字。Lock是一个接口。
  • synchronized不需要用户去手动释放锁,发生异常或者线程结束时自动释放锁;Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。
  • lock可以配置公平策略,实现线程按照先后顺序获取锁。
  • 提供了trylock方法 可以试图获取锁,获取到或获取不到时,返回不同的返回值 让程序可以灵活处理。
  • lock()和unlock()可以在不同的方法中执行,可以实现同一个线程在上一个方法中lock()在后续的其他方法中unlock(),比syncronized灵活的多。

6.如何实现所有线程在某个线程发生了之后再执行

1.闭锁CountDownLatch闭锁是典型的等待事件发生的同步工具类,将闭锁的初始值设置1,所有线程调用await方法等待,当事件发生时调用countDown将闭锁值减为0,则所有await等待闭锁的线程得以继续执行。

2.阻塞队列BlockingQueue所有等待事件的线程尝试从空的阻塞队列获取元素,将阻塞,当事件发生时,向阻塞队列中同时放入N个元素(N的值与等待的线程数相同),则所有等待的线程从阻塞队列中取出元素后得以继续执行。

3.信号量Semaphore设置信号量的初始值为等待的线程数N,一开始将信号量申请完,让剩余的信号量为0,待事件发生时,同时释放N个占用的信号量,则等待信号量的所有线程将获取信号量得以继续执行。

4.栅栏CyclicBarrier设置栅栏的初始值为1,当事件发生时,调用barrier.wait()冲破设置的栅栏,将调用指定的Runable线程执行,在该线程中启动N个新的子线程执行。这个方法并不是让执行中的线程全部等待在某个点,待某一事件发生后继续执行。

特别注意:不能用“条件队列”,多个线程阻塞等待在条件队列上,事件发生时调用“条件队列”的notifyAll方法或者signalAll方法虽然能唤醒所有等待线程,但是只有一个线程能够获得该条件队列的锁得以调度执行,其它线程未获得锁仍将继续阻塞等待。

7.如何保证多线程下的i++结果正确

1.使用 synchronized 关键字

public class MyThread implements Runnable {  
    private int i;

    public MyThread(int i) {  
        this.i = i;  
    }

    @Override  
    public void run() {  
        synchronized (this) {  
            System.out.println(i++);  
        }  
    }  
}

2.Atomic变量

使用AtomicInteger类来代替int类型,可以保证原子性。AtomicInteger类提供了线程安全的原子操作方法,如incrementAndGet()。

private AtomicInteger i = new AtomicInteger(0);

public void increment() {
    i.incrementAndGet();
}

3.Lock对象

使用Lock对象来对共享变量进行同步,保证同一时刻只有一个线程能够访问该变量。

private Lock lock = new ReentrantLock();

public void increment() {
    lock.lock();
    try {
        i++;
    } finally {
        lock.unlock();
    }
}

4.ThreadLocal变量

使用ThreadLocal变量来对共享变量进行封装,每个线程都拥有自己的副本,可以保证线程安全。

private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
    @Override
    protected Integer initialValue() {
        return 0;
    }
};

public void increment() {
    threadLocal.set(threadLocal.get() + 1);
}

8.volatile是怎么知道多线程下数据变更的

volatile 关键字用于标记一个变量是 volatile 的,这意味着这个变量的值可能会被多个线程同时修改,因此需要及时更新到内存中,以确保其他线程能够及时看到值的变化。

  • 当一个线程访问一个 volatile 变量时,Java 虚拟机会自动将该变量的值复制到线程本地内存中,以便其他线程能够访问该变量的值。当一个线程修改了 volatile 变量的值后,Java 虚拟机也会自动将修改后的值复制回线程本地内存中,以便其他线程能够更新他们的本地内存。
  • 由于 volatile 变量的值会被及时更新到内存中,因此其他线程可以及时看到该变量的值发生变化。如果一个变量不是 volatile 的,那么其他线程只能读取该变量的先前值,而不能直接读取最新的值,因为这样需要从磁盘或者其他慢速存储介质中读取数据,会降低程序的性能。
  • 需要注意的是,虽然 volatile 能够确保多线程下数据变更的正确性,但它并不能保证线程的安全性。如果在多线程环境下,修改一个非 volatile 变量的值可能会引起竞态条件等问题,因此需要谨慎使用 volatile 关键字。

9.线程池的执行流程和线程池的拒绝策略和核心配置参数

corePoolSize:核心线程数

maxiumPoolSize:最大线程数

keepAliveTime:空闲存活时间

unit: 时间单位

workQueue:任务队列
threadFactory: 线程工厂

handler:拒绝策略

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

  1. 判断当前线程数和核心线程数.
  2. 判断当前任务队列是否已满.
  3. 判断当前线程数是否已达最大线程数.

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

二. 拒绝策略当任务过多且线程池的任务队列已满时, 此时就会执行线程池的拒绝策略, 线程池的拒绝策略默认有以下 4 种:

AbortPolicy:中止策略,线程池会抛出异常并中止执行此任务.CallerRunsPolicy:调用方运行策略,把任务交给添加此任务的(main)线程来执行.DiscardPolicy:放弃最新策略,忽略此任务,忽略最新的一个任务.DiscardOldestPolicy:放弃最旧策略,忽略最早的任务,最先加入队列的任务.

  1. AbortPolicy(中止策略)功能: 当触发拒绝策略时,直接抛出拒绝执行的异常,中止策略的意思也就是打断当前执行流程
  2. CallerRunsPolicy(调用者运行策略)功能:当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程处理.
  3. DiscardPolicy(丢弃策略)功能:直接静悄悄的丢弃这个任务,不触发任何动作.
  4. DiscardOldestPolicy(弃老策略)功能:如果线程池未关闭,就弹出队列头部的元素,然后尝试执行.

一般情况下,任务分为IO密集型和CPU密集型。

CPU密集型:一般设置为cpu核数+1

IO密集型:一般设置为2n,以IO能力为主。

10.线程创建的几种方法

  • 继承Thread
  • 实现Runnable接口
  • 实现Callable接口,结合 FutureTask使用
  • 利用该线程池ExecutorService、Callable、Future来实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1290211.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python中字符串拼接及其应用场景

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 字符串拼接是Python中常见而重要的操作&#xff0c;它涉及到将多个字符串连接成一个字符串。本文将深入探讨Python中字符串拼接的不同方式、性能比较、以及在实际应用中的场景和最佳实践。 常见的字符串拼接方法…

【JUC】二十一、CAS比较并交换

文章目录 1、初体验2、CAS概述3、Unsafe类4、Unsafe汇编5、原子引用AutomicReference6、手写自旋锁SpinLock7、CAS的两大缺点8、AtomicStampedReference类解决ABA问题 1、初体验 没有CAS时&#xff0c;多线程环境下不使用原子类保证线程安全&#xff0c;比如i&#xff0c;可以…

21章网络通信

21.1——网络程序设计基础 网络程序设计编写得到是与其他计算机进行通信的程序 21.1.1——局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机 21.1.2——网络协议 网络协议规定了计算机之间连接的物理、机械 (网线与网卡的连接规定)、…

云上守沪 | 云轴科技ZStack成功实践精选(上海)

为打造国际数字之都&#xff0c;上海发布数字经济发展“十四五”规划&#xff0c;围绕数字新产业、数据新要素、数字新基建、智能新终端等重点领域&#xff0c;加强数据、技术、企业、空间载体等关键要素协同联动&#xff0c;加快进行数字经济发展布局&#xff1b;加快基础软件…

Linux环境下的MySQL安装

文章目录 前提说明1.卸载内置环境2.检查系统安装包3.卸载这些默认安装包4.获取MySQL官方yum源5.安装MySQLyum源&#xff0c;对比前后yum源6.查看yum源是否生效7.安装MySQL服务8.查看相对应的配置文件9.启动服务10.查看启动服务11.登录方法一12.登录方法二13.登录方法三14.设置开…

护理简历自我评价15篇

自我评价示例1&#xff1a; 我性格开朗&#xff0c;上进心强&#xff0c;做事不马虎&#xff0c;有良好的思想道德&#xff0c;注重集体荣誉感。我具备强大的护理技能和团队协作精神&#xff0c;能够在高压环境下保持冷静&#xff0c;积极应对挑战。我期待着在医疗领域发挥我的…

MySQL数据库与其管理工具Navicat

这里介绍MySQL数据库和Navicat的使用 1.下载MySQL数据库及MySQL客户端管理工具Navicat 登录www.mysql.com下载MySQL 登录www.navicat.com.cn/download下载客户端管理工具 2.启动MySQL数据库服务器 以管理员身份打开命令提示窗口 找到mysql的bin目录 输入初始化命令mysqld…

认识jmeter接口测试工具!

jmeter简介 Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于Web应用测试&#xff0c;但后来扩展到其他测试领域。 下载 下载地址&#xff1a;Apache JMeter - Download Apache JMeter 安装 由于Jmeter是基于Java的…

springboot 整合 Spring Security+JWT 实现token 认证和校验

1.大概是这个样子 JWT 是什么&#xff1f; Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准&#xff08;(RFC 7519).该token被设计为紧凑且安全的&#xff0c;特别适用于分布式站点的单点登录&#xff08;SSO&#xff09;场景。JWT的声明…

设计原则 | 依赖转置原则

一、依赖转置原则&#xff08;DIP&#xff1a;Dependence Inversion Principle&#xff09; 1、原理 高层模块不应该依赖低层模块&#xff0c;二者都应该依赖于抽象抽象不应该依赖于细节&#xff0c;细节应该依赖于抽象 2、层次化 Booch曾经说过&#xff1a;所有结构良好的面…

HTML5+CSS3+JS小实例:数字滑动选择控件

实例:数字滑动选择控件 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content=&quo…

【数据结构(八)】哈希表

文章目录 1. 基本概念1.1. 哈希表基本介绍 2. 实例应用2.1. 思路分析2.2. 代码实现2.2.1. 实现添加、显示功能2.2.2. 实现查找功能 1. 基本概念 先看一个实际需求&#xff1a; google 公司的一个上机题&#xff1a;     有一个公司&#xff0c;当有新的员工来报道时&…

Centos7部署NFS服务

搭建NFS存储服务器--基于CentOS7系统 - jianmuzi - 博客园 在CentOS中搭建NFS - 陌上荼靡 - 博客园 NFS简介 NFS 是 Network FileSystem 的缩写&#xff0c;顾名思义就是网络文件存储系统&#xff0c;它最早是由 Sun 公司发展出来的&#xff0c;也是 FreeBSD 支持的文件系统…

6.1810: Operating System Engineering 2023 <Lab3: page tables>

一、本节任务 实验环境&#xff1a; 二、要点 如何防止程序破坏内核或其他进程空间&#xff1f;隔离地址空间&#xff0c;进程只能读写自己的内存空间。 在保证隔离的同时&#xff0c;如何将多个地址空间复用到一个物理内存上&#xff1f;虚拟内存/页表。操作系统通过页表来为…

BUUCTF 加固题 babypython WriteUp

原题wp参考链接&#xff1a;https://www.cnblogs.com/karsa/p/13529769.html 这是CISCN2021 总决赛的题&#xff0c;解题思路是软链接zip 读取文件&#xff0c;然后伪造admin的session读取flag 回到buuctf的这个题&#xff1a; ssh连上去&#xff0c;查看 文件 /app/y0u_fou…

分割算法-大津算法

分割算法-大津算法 一、什么是大津算法二、算法原理三、公式推导四、代码五、算法适用性 大津算法介绍以及C函数代码实现。 一、什么是大津算法 大津算法&#xff08;Otsu&#xff09;由日本学者大津展之在1979年提出&#xff0c;又称最大类间方差法。此法求得的阈值&#xff…

Lidar-SLAM的历史与现状

文章&#xff1a;LiDAR-based SLAM for robotic mapping: state of the art and new frontiers 作者&#xff1a;Xiangdi Yue and Miaolei He 编辑&#xff1a;点云PCL 欢迎各位加入知识星球&#xff0c;获取PDF论文&#xff0c;欢迎转发朋友圈。文章仅做学术分享&#xff0c…

MOS管加三个元件就组成BUCK电路,为何说难点在于电感?

只要是电子产品就需要供电&#xff0c;就离不开电源&#xff0c;那什么是电源&#xff1a;小到手表中的电子&#xff0c;遥控器的电源&#xff0c;大到220V家庭用电&#xff0c;都可以看做是电源。然而在我们的电路设计中&#xff0c;会用到各种芯片&#xff0c;各种芯片所需要…

什么是呼叫中心的语音通道?呼叫中心语音线路有几种?

什么是呼叫中心的语音通道&#xff1f; 呼叫中心的语音通道是指在呼叫中心中使用的语音信号传输通道&#xff0c;它是呼叫中心中至关重要的一部分&#xff0c;负责将客户的语音信息传递给客服代表&#xff0c;以及将客服代表的语音信息传递给客户。在呼叫中心的运营中&#xf…

探索鸿蒙 DevEcoStudio汉化+运行报错

在下载好软件&#xff0c;摸索着成功创建了一个项目的时候&#xff0c;点击运行&#xff0c;竟然失败了。而且一大堆的英文也不知道从何入手&#xff0c;从网上搜了一下&#xff0c;找到了汉化的办法&#xff0c;并且解决了问题。我这里走的是Mac的步骤&#xff0c;微软的其实一…