【转】【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看...

2023-05-16

有读者跟我说,喜欢看我的文章,说很容易读,我确实在易读性上花费的心思不亚于在内容上。因为我不喜欢一上来就堆很多东西,而且把简单的东西搞得复杂人人都会,但是把复杂的东西讲的简单,确实需要非常多的思考。



不是线程的安全

面试官问:“什么是线程安全”,如果你不能很好的回答,那就请往下看吧。

论语中有句话叫“学而优则仕”,相信很多人都觉得是“学习好了可以做官”。然而,这样理解却是错的。切记望文生义。

同理,“线程安全”也不是指线程的安全,而是指内存的安全。为什么如此说呢?这和操作系统有关。

目前主流操作系统都是多任务的,即多个进程同时运行。为了保证安全,每个进程只能访问分配给自己的内存空间,而不能访问别的进程的,这是由操作系统保障的。

在每个进程的内存空间中都会有一块特殊的公共区域,通常称为堆(内存)。进程内的所有线程都可以访问到该区域,这就是造成问题的潜在原因。

假设某个线程把数据处理到一半,觉得很累,就去休息了一会,回来准备接着处理,却发现数据已经被修改了,不是自己离开时的样子了。可能被其它线程修改了。

比如把你住的小区看作一个进程,小区里的道路/绿化等就属于公共区域。你拿1万块钱往地上一扔,就回家睡觉去了。睡醒后你打算去把它捡回来,发现钱已经不见了。可能被别人拿走了。

因为公共区域人来人往,你放的东西在没有看管措施时,一定是不安全的。内存中的情况亦然如此。

所以线程安全指的是,在堆内存中的数据由于可以被任何线程访问到,在没有限制的情况下存在被意外修改的风险。

即堆内存空间在没有保护机制的情况下,对多线程来说是不安全的地方,因为你放进去的数据,可能被别的线程“破坏”。

那我们该怎么办呢?解决问题的过程其实就是一个取舍的过程,不同的解决方案有不同的侧重点。


私有的东西就不该让别人知道


现实中很多人都会把1万块钱藏着掖着,不让无关的人知道,所以根本不可能扔到大马路上。因为这钱是你的私有物品。

在程序中也是这样的,所以操作系统会为每个线程分配属于它自己的内存空间,通常称为栈内存,其它线程无权访问。这也是由操作系统保障的。

如果一些数据只有某个线程会使用,其它线程不能操作也不需要操作,这些数据就可以放入线程的栈内存中。较为常见的就是局部变量。










double avgScore(double[] scores) {    double sum = 0;    for (double score : scores) {        sum += score;    }    int count = scores.length;    double avg = sum / count;    return avg;}


这里的变量sum,count,avg都是局部变量,它们都会被分配在线程栈内存中。

假如现在A线程来执行这个方法,这些变量会在A的栈内存分配。与此同时,B线程也来执行这个方法,这些变量也会在B的栈内存中分配。

也就是说这些局部变量会在每个线程的栈内存中都分配一份。由于线程的栈内存只能自己访问,所以栈内存中的变量只属于自己,其它线程根本就不知道。

就像每个人的家只属于自己,其他人不能进来。所以你把1万块钱放到家里,其他人是不会知道的。且一般还会放到某个房间里,而不是仍在客厅的桌子上。

所以把自己的东西放到自己的私人地盘,是安全的,因为其他人无法知道。而且越隐私的地方越好。


大家不要抢,人人有份


相信聪明的你已经发现,上面的解决方案是基于“位置”的。因为你放东西的“位置”只有你自己知道(或能到达),所以东西是安全的,因此这份安全是由“位置”来保障的。

在程序里就对应于方法的局部变量。局部变量之所以是安全的,就是因为定义它的“位置”是在方法里。这样一来安全是达到了,但是它的使用范围也就被限制在这个方法里了,其它方法想用也不用了啦。

现实中往往会有一个变量需要多个方法都能够使用的情况,此时定义这个变量的“位置”就不能在方法里面了,而应该在方法外面。即从(方法的)局部变量变为(类的)成员变量,其实就是“位置”发生了变化。

那么按照主流编程语言的规定,类的成员变量不能再分配在线程的栈内存中,而应该分配在公共的堆内存中。其实也就是变量在内存中的“位置”发生了变化,由一个私有区域来到了公共区域。因此潜在的安全风险也随之而来。

那怎么保证在公共区域的东西安全呢?答案就是,大家不要抢,人人有份。设想你在街头免费发放矿泉水,来了1万人,你却只有1千瓶水,结果可想而知,一拥而上,场面失守。但如果你有10万瓶水,大家一看,水多着呢,不用着急,一个个排着队来,因为肯定会领到。

东西多了,自然就不值钱了,从另一个角度来说,也就安全了。大街上的共享单车,现在都很安全,因为太多了,到处都是,都长得一样,所以连搞破坏的人都放弃了。因此要让一个东西安全,就疯狂的copy它吧。

回到程序里,要让公共区域堆内存中的数据对于每个线程都是安全的,那就每个线程都拷贝它一份,每个线程只处理自己的这一份拷贝而不去影响别的线程的,这不就安全了嘛。相信你已经猜到了,我要表达的就是ThreadLocal类了。








































class StudentAssistant {
ThreadLocal<String> realName = new ThreadLocal<>(); ThreadLocal<Double> totalScore = new ThreadLocal<>();
String determineDegree() { double score = totalScore.get(); if (score >= 90) { return "A"; } if (score >= 80) { return "B"; } if (score >= 70) { return "C"; } if (score >= 60) { return "D"; } return "E"; }
double determineOptionalcourseScore() { double score = totalScore.get(); if (score >= 90) { return 10; } if (score >= 80) { return 20; } if (score >= 70) { return 30; } if (score >= 60) { return 40; } return 60; }}


这个学生助手类有两个成员变量,realName和totalScore,都是ThreadLocal类型的。每个线程在运行时都会拷贝一份存储到自己的本地。

A线程运行的是“张三”和“90”,那么这两个数据“张三”和“90”是存储到A线程对象(Thread类的实例对象)的成员变量里去了。假设此时B线程也在运行,是“李四”和“85”,那么“李四”和“85”这两个数据是存储到了B线程对象(Thread类的实例对象)的成员变量里去了。

线程类(Thread)有一个成员变量,类似于Map类型的,专门用于存储ThreadLocal类型的数据。从逻辑从属关系来讲,这些ThreadLocal数据是属于Thread类的成员变量级别的。从所在“位置”的角度来讲,这些ThreadLocal数据是分配在公共区域的堆内存中的。

说的直白一些,就是把堆内存中的一个数据复制N份,每个线程认领1份,同时规定好,每个线程只能玩自己的那份,不准影响别人的。

需要说明的是这N份数据都还是存储在公共区域堆内存里的,经常听到的“线程本地”,是从逻辑从属关系上来讲的,这些数据和线程一一对应,仿佛成了线程自己“领地”的东西了。其实从数据所在“位置”的角度来讲,它们都位于公共的堆内存中,只不过被线程认领了而已。这一点我要特地强调一下。

其实就像大街上的共享单车。原来只有1辆,大家抢着骑,老出问题。现在从这1辆复制出N辆,每人1辆,各骑各的,问题得解。共享单车就是数据,你就是线程。骑行期间,这辆单车从逻辑上来讲是属于你的,从所在位置上来讲还是在大街上这个公共区域的,因为你发现每个小区大门口都贴着“共享单车,禁止入门”。哈哈哈哈。

共享单车是不是和ThreadLocal很像呀。再重申一遍,ThreadLocal就是,把一个数据复制N份,每个线程认领一份,各玩各的,互不影响。


只能看,不能摸


放在公共区域的东西,只是存在潜在的安全风险,并不是说一定就不安全。有些东西虽然也在公共区域放着,但也是十分安全的。比如你在大街上放一个上百吨的石头雕像,就非常安全,因为大家都弄不动它。

再比如你去旅游时,经常发现一些珍贵的东西,会被用铁栅栏围起来,上面挂一个牌子,写着“只能看,不能摸”。当然可以国际化一点,“only look,don't touch”。这也是很安全的,因为光看几眼是不可能看坏的。

回到程序里,这种情况就属于,只能读取,不能修改。其实就是常量或只读变量,它们对于多线程是安全的,想改也改不了。





class StudentAssistant {
final double passScore = 60;}


比如把及格分数设定为60分,在前面加上一个final,这样所有线程都动不了它了。这就很安全了。


小节一下以上三种解决方案,其实都是在“耍花招”。

第一种,找个只有自己知道的地方藏起来,当然安全了。

第二种,每人复制1份,各玩各的,互不影响,当然也安全了。

第三种,更狠了,直接规定,只能读取,禁止修改,当然也安全了。

是不是都在“避重就轻”呀。如果这三种方法都解决不了,该怎么办呢?Don't worry,just continue reading。


没有规则,那就先入为主


前面给出的三种方案,有点“理想化”了。现实中的情况其实是非常混乱嘈杂的,没有规则的。

比如在中午高峰期你去饭店吃饭,进门后发现只剩一个空桌子了,你心想先去点餐吧,回来就坐这里吧。当你点完餐回来后,发现已经被别人捷足先登了。

因为桌子是属于公共区域的物品,任何人都可以坐,那就只能谁先抢到谁坐。虽然你在人群中曾多看了它一眼,但它并不会记住你容颜。

解决方法就不用我说了吧,让一个人在那儿看着座位,其它人去点餐。这样当别人再来的时候,你就可以理直气壮的说,“不好意思,这个座位,我,已经占了”。

我再次相信聪明的你已经猜到了我要说的东西了,没错,就是(互斥)锁。

回到程序里,如果公共区域(堆内存)的数据,要被多个线程操作时,为了确保数据的安全(或一致)性,需要在数据旁边放一把锁,要想操作数据,先获取锁再说吧。

假设一个线程来到数据跟前一看,发现锁是空闲的,没有人持有。于是它就拿到了这把锁,然后开始操作数据,干了一会活,累了,就去休息了。

这时,又来了一个线程,发现锁被别人持有着,按照规定,它不能操作数据,因为它无法得到这把锁。当然,它可以选择等待,或放弃,转而去干别的。

第一个线程之所以敢大胆的去睡觉,就是因为它手里拿着锁呢,其它线程是不可能操作数据的。当它回来后继续把数据操作完,就可以把锁给释放了。锁再次回到空闲状态,其它线程就可以来抢这把锁了。还是谁先抢到锁谁操作数据。


















class ClassAssistant {
double totalScore = 60; final Lock lock = new Lock();
void addScore(double score) { lock.obtain(); totalScore += score; lock.release(); }
void subScore(double score) { lock.obtain(); totalScore -= score; lock.release(); }}


假定一个班级的初始分数是60分,这个班级抽出10名学生来同时参加10个不同的答题节目,每个学生答对一次为班级加上5分,答错一次减去5分。因为10个学生一起进行,所以这一定是一个并发情形。

因此加分和减分这两个方法被并发的调用,它们共同操作总分数。为了保证数据的一致性,需要在每次操作前先获取锁,操作完成后再释放锁。


相信世界充满爱,即使被伤害


再回到一开始的例子,假如你往地上仍1万块钱,是不是一定会丢呢?这要看情况了,如果是在人来人往的都市,可以说肯定会丢的。如果你跑到无人区扔地上,可以说肯定不会丢。

可以看到,都是把东西无保护的放到公共区域里,结果却相差很大。这说明安全问题还和公共区域的环境状况有关系。

比如我把数据放到公共区域的堆内存中,但是始终都只会有1个线程,也就是单线程模型,那这数据肯定是安全的。

再者说,2个线程操作同一个数据和200个线程操作同一个数据,这个数据的安全概率是完全不一样的。肯定线程越多数据不安全的概率越大,线程越少数据不安全的概率越小。取个极限情况,那就是只有1个线程,那不安全概率就是0,也就是安全的。

可能你又猜到了我想表达的内容了,没错,就是CAS。可能大家觉得既然锁可以解决问题,那就用锁得了,为啥又冒出了个CAS呢?

那是因为锁的获取和释放是要花费一定代价的,如果在线程数目特别少的时候,可能根本就不会有别的线程来操作数据,此时你还要获取锁和释放锁,可以说是一种浪费。

针对这种“地广人稀”的情况,专门提出了一种方法,叫CAS(Compare And Swap)。就是在并发很小的情况下,数据被意外修改的概率很低,但是又存在这种可能性,此时就用CAS。

假如一个线程操作数据,干了一半活,累了,想要去休息。(貌似今天的线程体质都不太好)。于是它记录下当前数据的状态(就是数据的值),回家睡觉了。

醒来后打算继续接着干活,但是又担心数据可能被修改了,于是就把睡觉前保存的数据状态拿出来和现在的数据状态比较一下,如果一样,说明自己在睡觉期间,数据没有被人动过(当然也有可能是先被改成了其它,然后又改回来了,这就是ABA问题了),那就接着继续干。如果不一样,说明数据已经被修改了,那之前做的那些操作其实都白瞎了,就干脆放弃,从头再重新开始处理一遍。

所以CAS这种方式适用于并发量不高的情况,也就是数据被意外修改的可能性较小的情况。如果并发量很高的话,你的数据一定会被修改,每次都要放弃,然后从头再来,这样反而花费的代价更大了,还不如直接加锁呢。

这里再解释下ABA问题,假如你睡觉前数据是5,醒来后数据还是5,并不能肯定数据没有被修改过。可能数据先被修改成8然后又改回到5,只是你不知道罢了。对于这个问题,其实也很好解决,再加一个版本号字段就行了,并规定只要修改数据,必须使版本号加1。

这样你睡觉前数据是5版本号是0,醒来后数据是5版本号是0,表明数据没有被修改。如果数据是5版本号是2,表明数据被改动了2次,先改为其它,然后又改回到5。

我再次相信聪明的你已经发现了,这里的CAS其实就是乐观锁,上一种方案里的获取锁和释放锁其实就是悲观锁。乐观锁持乐观态度,就是假设我的数据不会被意外修改,如果修改了,就放弃,从头再来。悲观锁持悲观态度,就是假设我的数据一定会被意外修改,那干脆直接加锁得了。

作者观点


前两种属于隔离法,一个是位置隔离,一个是数据隔离。

然后两种是标记法,一个是只读标记,一个是加锁标记。

最后一种是大胆法,先来怼一把试试,若不行从头再来。

对于大胆法,还是有必要尝试的。有人曾说过,“梦想还是要有的,万一实现了呢”。


(END)

非常感谢本文的作者 李新杰 老师,原文链接:https://www.cnblogs.com/lixinjie/p/a-answer-about-thread-safety-in-a-interview.html

转载于:https://www.cnblogs.com/Taskr212/p/11440473.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【转】【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看... 的相关文章

  • Ubuntu 安装和配置 git

    安装 sudo apt get update y sudo apt get upgrade y sudo apt install git 检查 git version 配置 git config global user name 34 用户
  • Centos检查进程是否关闭的shell脚本

    在运维工作当中会遇到程序打不开或者不工作的类似情况 xff0c 有部分原因是因为维持这个服务的某个进程关闭了 xff0c 这时候需要检查哪些进程是否挂掉了 xff0c 由于进程较多一个个来检查的话效率太慢 在这里给伙伴们安利一个简单实用的检
  • 基于SpringBoot和vue的移动端音乐网站的设计与实现

    1 xff0c 项目介绍 基于SpringBoot和vue的移动端音乐网站拥有两种角色 xff0c 分别为管理员和用户 用户 xff1a 歌单分类 xff1b 歌手分类 xff1b 仿网易云音乐播放 xff1b 音乐收藏 xff1b 音乐评
  • 前端-01Html5基本知识

    1 基本 1 1 第一个前端程序 内容 lt html gt lt head gt lt title gt 我的网页 lt title gt lt head gt lt body gt Hello 我的第一个网页 lt body gt lt
  • 使用maven来进行JavaSE增删改查

    首先得搞清楚maven是什么 xff1f Maven是一款服务于Java平台的自动化构建工具 Maven 作为 Java 项目管理工具 xff0c 可以用作包管理 xff0c 可以支持整个项目的开发 打包 测试及部署等一系列行为 maven
  • 银河麒麟桌面操作系统V10上安装的docker常规使用及注意事项

    前言 本文介绍银河麒麟桌面系统V10上docker的常规使用及注意事项 一 docker信息 银河麒麟桌面系统V10预装的docker xff0c 且开机自动启动了docker服务 查看服务状态 systemctl status docke
  • 背包九讲PDF

    本资料仅限个人学习交流使用 xff0c 不得用于商业用途 背包九讲PDF xff1a https pan baidu com s 17rTxMwCo9iSTOW77yucdXQ 提取码 xff1a xbqa 转载于 https www cn
  • electron在Windows、Linux和KYLIN操作系统下的不同表现

    一 electron简介 Electron 是一个由 Github 开发 用 HTML xff0c CSS 和 JavaScript 来构建跨平台桌面应用程序的框架 xff0c 然后这些应用程序可以打包在macOS Windows和Linu
  • vscode 终端美化

    1 进入网站 Base16 Terminal Colors for Visual Studio Code 2 选择自己喜欢的主题 点击Copy to clipboard 3 打开vscode 设置 输入setting 在 settings
  • kali美化与配置

    kali linux简单美化 前言 xff1a kali linux是一个神奇的系统 xff0c 里面含有大量的工具 xff08 虽然python很容易就可以做出来 xff09 xff0c 但是像msf这样的大作还是很有参考价值的 好不容易
  • ubuntu开机进入循环登录状态的解决方案

    ubuntu进入循环登陆的解决方案 1 出现问题的原因2 解决问题的方法2 1 登陆进入CLI2 2 检查 etc environment环境变量 1 出现问题的原因 我出现的这个问题的原因是由于修改系统的环境变量 etc profile
  • Tomcat 7.X安装教程(简单易懂)

    Tomcat 7 X安装教程 简单易懂 步骤1 下载Tomcat7 x版本 官网7 x下载地址 https tomcat apache org download 70 cgi https aoian lanzous com iCWyymcn
  • 社区医疗管理系统(JDBC+eclipse)

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 项目预览1 登陆界面2 首页3 修改密码和查看个人信息4 用户管理5 居民信息 二 导入步骤1 下载文件2 数据库导入3
  • linux shell 常用命令

    linux shell 终端操作命令 shutdown 默认1分钟内关机 43 n表示n分钟后关机 输入后可以打shutdown c 进行取消 shutdown h now表示立即关机sudo 在命令前书写 xff0c 表示已管理员的权限运
  • Shell脚本——kafka集群启停

    部署kafka集群的服务器名称为 xff1a node101 node102 node103 批量启动 停止zookeeper xff0c 查看zookeeper开启状态 在 opt module zookeeper bin 目录中 xff
  • Shell脚本——批量关闭服务器

    服务器名称为 xff1a node101 node105 node110 node113 bin bash array 61 102 103 104 105 110 113 101 xff09 for i 61 0 i lt array i
  • 设置文件夹共享

    本文使用共享文件夹名称为 xff1a test 1 右键文件夹 xff0c 点击 属性 xff0c 菜单栏选择 共享 xff0c 点击下面的 共享 S 按钮 2 选择要共享的用户 xff08 此处选择 Everyone xff0c 可以根据
  • Ubuntu16.04的图形化界面无法启动问题

    昨晚在 Ubuntu 下试图安装笔记本触控板的驱动的时候 xff0c 突然 Ubuntu 的图形化界面不见了 xff0c 尝试了 Ctrl 43 Alt 43 F1 F2 F3 无果 xff0c 又在一些博客的指导下尝试在命令行使用 sta
  • Shell脚本——设置ssh免密

    功能 xff1a 实现多台linux主机之间root用户的免密设置 主机名 xff1a node101 node106 在node101上切换至root用户 xff0c 编写以下脚本内容 xff1a bin bash 使用root用户 在n
  • 解析ET6接入ILRuntime实现热更

    1 介绍 ILRuntime项目为基于C 的平台 xff08 例如Unity xff09 提供了一个纯C 实现 xff0c 快速 方便且可靠的IL运行时 xff0c 使得能够在不支持JIT的硬件环境 xff08 如iOS xff09 能够实

随机推荐

  • Python 实现用网页展示多个结果表数据

    Python 实现用网页展示多个表格的数据 前言 一 效果图 二 代码 1 引入库 2 函数定义 3 主程序 前言 实现方法是利用pandas to html 与表格展示的美化相结合 使数据展示更美观 一 效果图 示例 二 代码 本文将使用
  • error while loading shared libraries

    问题描述 我在调试配置一个Linux计算环境的程序时候 xff0c 安装配置好相关的库 xff0c 但是在执行运行程序命令时候报错如下 xff1a error span class token keyword while span load
  • 要求在数组中间删除一个数字

    span class token comment 要求在数组中间删除一个数字 span span class token keyword var span arr span class token operator 61 span span
  • Ubuntu常见问题及解决办法

    在刚开始使用Ubuntu系统时 xff0c 总会遇到各种各样的小问题 xff0c 这里整理了一些遇到的问题及解决办法 xff0c 不断更新中 xff01 xff01 xff01 目录 一 创建文件夹权限不够 1 1 问题描述 1 2 解决办
  • 计算机网络考试题库

    第1章 计算机网络概论 1 xff0e 在20世纪50年代 xff0c 和 xff08 xff09 技术的互相结合 xff0c 为计算机网络的产生奠定了理论基础 2 xff0e 从传输范围的角度来划分计算机网络 xff0c 计算机网络可以分
  • Spring,搭建Spring环境

    控制反转 xff1a 控制了对象的创建 xff0c 反转 xff1a 反转的是获取对象的方式 xff0c 从自己创建对象变为由Spring工厂推送 1 搭建Spring环境 xff0c 导入依赖 spring aop xff1a 开发AOP
  • 【Vxworks操作系统】系统介绍与系统组成-NO.1

    目录 1 VxWorks系统介绍 2 VxWorks特点 3 vxWorks操作系统的组成 1 xff09 实时操作系统核心Wind 2 xff09 I O系统 3 xff09 文件系统 4 xff09 板级支持包 xff08 BSP xf
  • 【Vxworks操作系统】实时多任务介绍-NO.2

    目录 实时多任务 1 1 任务生命周期管理 1 2 任务状态控制 1 3 任务调度 1 4 用户接口 结语 xff1a 实时操作系统是基于多任务和任务间通信的概念的操作系统 xff0c 多任务环境允许一个实时应用由一组各自独立的任务组成 x
  • HDU 6298(数学)

    题意是给出一个数 xff0c 找出这个数的三个因子且这三个因子的和等于这个数 xff0c 输出满足条件的乘积最大的一组因子的乘积 xff0c 如果不存在这样的因子 xff0c 就输出 1 第一次 wa 了 xff0c 因为把题目中的 x n
  • libevent的http服务详解

    libevent的http服务详解 即远程调用 xff0c 得到一个call ID然后验证外部远程参数 span class token function evrpc request cb span span class token pun
  • 创建物理卷报错Can‘t open /dev/sdb1 exclusively. Mounted filesystem?以及对应的解决方法

    创建物理卷报错Can 39 t open dev sdb1 exclusively Mounted filesystem 以及对应的解决方法 一 xff1a 报错二 xff1a 解决方法 一 xff1a 报错 1 添加一块硬盘 2 对硬盘分
  • windows server 2012r2 开启远程桌面

    电脑 右键 属性 远程设置 允许远程连接此计算机 勾选 选择用户 添加administrator 开始 运行 gpedit msc 计算机配置 管理模板 Windows组件 远程桌面服务 连接 允许用户通过使用远程桌面服务进行远程连接 打开
  • Obsidian 多端同步实现

    原文地址 1 前言 如何将 windows 和 android 端的 obsidian 同步 xff1f 可以选择官方的 Obsidian Sync 服务 xff0c 或者使用 FolderSync 等同步工具 本文介绍一种基于 Git 的
  • Github Page 个人主页——项目部署

    本人博客 一 前言 想搭建自己的网站吗 xff1f 通常需要买一台服务器 xff0c 购买一个域名进行备案后 xff0c 解析到自己的服务器 xff0c 还要搭建环境 xff0c 后期运维等等 本文提供一种基于 Github Page 服务
  • Github Page 个人主页——CDN加速

    原文地址 1 前言 前两篇文章介绍了 如何部署静态网站 以及 给网站自定义域名 xff0c 到目前为止 xff0c 您已经拥有一个使用自己的域名的网站了 在访问个人网页时 xff0c 实质上是去Github的服务器上取资源的 xff0c 但
  • Github Page 个人主页——Hexo博客

    原文地址 一 前言 在前三篇文章介绍了如何部署一个属于自己的站点网页 xff0c 但是只有个人主页有些单调 xff0c Github Page 的本质是部署web站点 xff0c 所以不仅可以部署个人主页 xff0c 还可以将自己的博客部署
  • Android 开发——环境搭建

    原文地址 一 前言 1 1 Android Studio 简介 Android Studio 是谷歌推出的一个Android集成开发工具 xff0c 基于IntelliJ IDEA 类似 Eclipse ADT xff0c Android
  • 【Matlab】音频信号谱分析及椭圆滤波处理

    前言 一个使用matlab对音频信号进行频谱分析及滤波处理的学习笔记 xff0c 本文使用的是椭圆滤波器 音频下载 demo mp3 频谱分析 读取音频信号进行傅里叶变换 x fs 61 audioread 39 D demo mp3 39
  • 【Matlab】BPSK二进制相移键控波形生成

    前言 一个通信原理课程中使用Matlab生成BPSK波形的实验笔记 内容 设发送二进制信息为10011101 xff0c 码元速率为1波特 xff0c 载波 sin wt xff0c 幅值为1 xff0c 初始相位为0 当载波频率为2Hz
  • 【转】【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看...

    有读者跟我说 xff0c 喜欢看我的文章 xff0c 说很容易读 xff0c 我确实在易读性上花费的心思不亚于在内容上 因为我不喜欢一上来就堆很多东西 xff0c 而且把简单的东西搞得复杂人人都会 xff0c 但是把复杂的东西讲的简单 xf