Sean's Blog


  • Home

  • Tags

  • Categories

  • Archives

  • Search

Redis[2]多个数据库

Posted on 2019-09-29 | In Redis

Redis多个数据库

Redis支持多个数据库,每个数据库的数据是隔离的。

Redis的数据库是基于单机的,集群没有数据库的概念。

Redis多数据库解释

Redis是一个字典结构的存储服务器,而实际上一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。这与我们熟知的在一个关系数据库实例中可以创建多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库。

每个数据库对外都是一个从0开始的递增数字命名,Redis默认支持16个数据库(可以通过配置文件支持更多,无上限),可以通过配置database来修改这一数字。客户端与Redis建立连接后会自动选择0号数据库,不过可以随时使用SELECT命令更换数据库,如要选择1号数据库,就使用SELECT 1即可。

Redis数据库限制

这些以数字命名的数据库与我们理解的数据库有所区别:

  1. Redis不支持自定义数据库的名字,每个数据库都以编号命名,开发者必须自己记录哪些数据库存储了哪些数据。

  2. Redis不支持为每个数据库设置不同的访问密码,所以一个客户端要么可以访问全部数据库,要么一个也不能。

  3. 多个数据库之间不是完全隔离的,FLUSHALL命令可以清空一个Redis实例中所有数据库中的数据。

Redis数据库更像一种命名空间,而不适宜存储不同应用程序的数据。由于Redis非常轻量级,一个空Redis实例占用的内在只有1M左右,所以不用担心多个Redis实例会额外占用很多内存。

Redis[1]数据类型与常用命令

Posted on 2019-09-29 | In Redis

Redis 基础命令

命令 描述
redis-cli -h host -p port -a password Redis连接指令
CONFIG GET configname 得到配置值
CONFIG SET configname configvalue 设置配置值
SELECT dbname 切换数据库
FLUSHDB 清空数据库[危险]
FLUSHALL 清空所有数据库[危险]

Redis 键命令

命令 描述
KEYS pattern 查找所有符合给定模式(pattern)的key[危险]
EXISTS key 检查key是否存在
TYPE key 查看key对应值的类型
RENAME key newkey 修改key的名称
DEL key 在key存在时删除key
MOVE key db 将键移到目标数据库
EXPIRE key 设置过期时间(秒)

Redis数据类型

类型 简介 特性 场景
String(字符串) 二进制安全 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M —
Hash(字典) 键值对集合,即编程语言中的Map类型 适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值 存储、读取、修改用户属性
List(列表) 双向链表 增删快,提供了操作某一段元素的API 1. 最新消息排行等功能(如朋友圈的时间线)\n2. 消息队列
Set(集合) 哈希表实现,元素不重复 1. 添加、删除、查找的复杂度都是O(1)\n2.为集合提供了求交集、并集、差集等操作 1. 共同好友\n2.利用唯一性,统计访问网站的所有独立IP\n3.好友推荐时,根据tag求交集,大于某个阈值就可以推荐
Sorted Set(有序集合) 将Set中的元素增加一个权重参数score,元素按score有序排列 数据插入集合时,已经进行天然排序 1. 排行榜\n 2. 带权重的消息队列

字符串

命令 描述
SET key value 设置key为指定的字符串
APPEND key value 如果 key 已经存在,并且值为字符串,那么这个命令会把value追加到原来值(value)的结尾。如果不存在,那么类似SET
GET key 获取key值,不存在则返回nil
GETRANGE key start end 获取索引开始位置(包含)和结束位置(包含)所对应的值,索引从0开始
GETSET key value 设置新的key值,并获取设置之前的值,key不存在则设置,并返回nil
MGET key [key …] 批量获取key的值
MSET key value [key value…] 批量设置key的值
DECR key 数字类型key自减操作,key类型不是数字则报错
INCR key 数字类型key自增操作
STRLEN key 获取key值长度

hash

命令 描述
HSET key field value 设置hash表中字段的值
HMSET key field value [field value …] 批量设置hash表中字段的值
HSETNX key field value 只有当字段不存在的时候才设置hash表中字段值
HGET key field 获取hash表中字段的值
HMGET key field [field …] 批量获取hash表中字段的值
HGETALL key 获取hash表中的所有字段和值
HEXISTS key field 判断hash表中字段是否存在
HDEL key field [field …] 删除hash表中一个或多个字段
HLEN key 获取hash表中字段个数
HVALS key 获取hash表中所有字段的值
HKEYS key 获取hash表中所有的字段
HSTRLEN key field 获取hash表中指定字段的值的长度

list

命令 描述
LPUSH key value [value …] 从列表左边放入一个或者多个元素
LPUSHX key value 当列表存在时,从左边放入一个元素
RPUSH key value [value …] 从列表右边放入一个或者多个元素
RPUSHX key value 列表存在时,从右边放入一个元素
LSET key index value 根据索引位置设置列表中元素的值
LINDEX key index 根据列表索引获取元素值,索引从0开始
LINSERT key BEFORE\ AFTER pivot value 在列表中,基于某个基准点插入值
LLEN key 获取列表长度
LRANGE key start stop 根据索引获取列表中的元素
LREM key count value 从key移除前count个出现值为value的元素(count > 0 前到后, count < 0 后到前, count = 0 移除所有)
LPOP key 删除列表左边的一个元素
RPOP key 删除列表右边的一个元素

set

命令 描述
SADD key member [member …] 添加一个或多个元素到集合中
SREM key member [member …] 删除一个或多个集合中的元素
SCARD key 获取集合中元素数量
SMEMBERS key 返回集合中所有的元素
SINTER key [key …] 获取两个或两个以上集合的交集
SUNION key [key …] 获取两个或两个以上集合的并集
SDIFF key [key …] 获取两个或者两个以上集合的差集
SISMEMBER key member 判断元素是否是在指定集合中
SMOVE source destination member 移动一个集合中的元素到另一个集合

zset

命令 描述
ZADD key score member [score member] 向一个有序集合添加成员
ZCARD key 获取有序集合中元素个数
ZCOUNT key min max 指定分数范围的元素个数
ZINCRBY key increment member 为有序集的元素的score值加上指定的increment
ZRANGE key start stop [WITHSCORES] 根据有序看分数区间获取集合中的元素
ZRANK key member 得到成员的排名
ZREM key member [member …] 删除有序集合中一个或多个元素
ZSCORE key member 查询元素在集合中的分数

spring[1]spring事务的7种传播行为

Posted on 2019-09-29 | In spring

事务传播行为

事务传播行为(propagation behavior) 指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。

spring 的七种传播行为

传播行为 含义
REQUIRED 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
SUPPORTS 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

NESTED 和 REQUIRED_NEW

REQUIRED_NEW 是为子方法创建一个新的事务, NESTED 是为子方法创建一个嵌套事务。

在内层事务执行失败的时候,都不会引起外层事务的回滚。

但在外层事务执行失败时,REQUIRED_NEW 注解的内部方法由于已经提交了,不会进行回滚。

而NESTED将嵌套事务作为外层事务的一部分,会随着外层事务的失败而回滚。

项目搭建[7]Nginx负载均衡与Tomcat集群

Posted on 2019-09-29 | In 项目搭建

单机部署多个Tomcat实例

下载Tomcat并解压到相应的目录下

1
2
wget http://mirrors.hust.edu.cn/apache/tomcat/tomcat-9/v9.0.7/bin/apache-tomcat-9.0.7.tar.gz
tar -zxvf apache-tomcat-9.0.7.tar.gz

拷贝多份Tomcat目录

1
2
cp -r apache-tomcat-9.0.7 ./tomcat9-02
mv apache-tomcat-9.0.7 ./tomcat9-01

配置环境变量

1
2
3
4
5
6
7
8
9
10
11
vim /etc/profile  # 在文件末尾增加如下内容

export CATALINA_BASE=/usr/local/tomcat9-01
export CATALINA_HOME=/usr/local/tomcat9-01
export TOMCAT_HOME=/usr/local/tomcat9-01

export CATALINA_2_BASE=/usr/local/tomcat9-02
export CATALINA_2_HOME=/usr/local/tomcat9-02
export TOMCAT_2_HOME=/usr/local/tomcat9-02

source /ect/profile # 使配置文件生效

编辑第二个Tomcat的bin下的catalina.sh文件

1
2
3
4
5
cd /usr/local/tomcat9-02/bin/
vim catalina.sh # 找到如下那行注释,在该注释下,增加两行配置
# OS specific support. $var_must_be set to either true or false.
export CATALINA_BASE=$CATALINA_2_BASE
export CATALINA_HOME=$CATALINA_2_HOME

编辑第二个Tomcat的conf下的server.xml文件

1
2
3
4
5
6
7
8
9
10
cd ../conf/
vim server.xml
# 第一个端口, Server port 节点端口
<Server port="9005" shutdown="SHUTDOWN">
# 第二个端口, Connector port 节点端口, 也即是Tomcat访问端口
<Connector port="9080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8" />
# 第三个端口, Connector port节点端口
<Connector port="9009" protocol="AJP/1.3" redirectPort="8443" />

执行 bin目录下的startup.sh

1
2
cd ../bin/
./startup.sh

检查监听的端口号及进程

1
2
netstat -lntp | grep java
ps -aux | grep java

通过访问不同的端口,能够访问到Tomcat默认的首页

Nginx负载均衡配置,常用策略简介

轮询(默认)

  • 优点: 实现简单

  • 缺点: 不考虑每台服务器的处理能力

  • 配置:

1
2
3
4
5
upstream www.xxx.com {
# 需要负载的server列表
server www.xxx.com:8080;
server www.xxx.com:9080;
}

权重(使用最多)

  • 优点: 考虑了每台服务器处理能力的不同,哪台机器性能高就给哪台机器的权重高一些

  • 配置:

1
2
3
4
upstream www.xxx.com {
server www.xxx.com:8080 weight=15;
server www.xxx.com:9080 weight=10;
}

ip hash

  • 优点: 能实现同一个用户始终访问同一个服务器

  • 缺点: 根据ip hash 不一定平均

  • 配置:

1
2
3
4
5
6
upstream www.xxx.com {
ip_hash;
# 需要负载的server列表
server www.xxx.com:8080;
server www.xxx.com:9080;
}

url hash(第三方插件)

  • 优点: 能实现同一个服务访问同一个服务器,也就是根据url进行负载

  • 缺点: 和ip hash一样,根据url hash分配请求不一定平均,请求频繁的url会请求到同一台服务器上。

  • 配置:

1
2
3
4
5
6
upstream www.xxx.com {
# 需要负载的server 列表
server www.xxx.com:8080;
server www.xxx.com:9080;
hash $request_uri;
}

fair (第三方插件)

  • 特点: 按后端服务器的相应时间来分配请求,相应时间短的优先分配

  • 配置:

1
2
3
4
5
6
upstream www.xxx.com {
# 需要负载的server列表
server www.xxx.com:8080;
server www.xxx.com:9080;
fair;
}

负载均衡参数简介

1
2
3
4
5
6
7
upstream www.xxx.com {
ip_hash;
# 需要负载的server列表
server www.xxx.com:8080 down; # down 表示当前的server暂时不参与负载
server www.xxx.com:9080 weight=2; # weight默认值为1,weight的值越大,负载的权重就越大
server www.xxx.com:7080 backup; # 其他所有的非backup机器,在down掉或者很忙的时候,才请求backup机器
}

项目搭建[6]负载均衡与集群简介

Posted on 2019-09-27 | In 项目搭建

集群与负载均衡

在单一服务器中执行WEB程序可能会遇到以下两个问题:

  1. 网站接收大量请求时,单一服务器无法满足需要处理的负荷量。

  2. 如果该服务器出现故障,网站无法运作。

集群

集群(Cluster)是一组独立的计算机系统构成一个松耦合的多处理器系统,他们之间通过网络实现进程间的通信。应用程序可以通过网络共享内存进行消息传送,实现分布式计算机。

负载均衡

负载均衡(Load Balance)即将负载压力根据某种算法合理分配到集群中的每一台计算机上,以减轻主服务器的压力,降低对主服务器的硬件和软件要求。

在负载均衡下,每台服务器都具有同等的地位,可以单独对外提供服务而无需其他服务器的辅助。通过负载分担技术,将外部发送来的请求按一定规则分配到对称结构中的某一台服务器上,而接收到请求的服务器都独立回应客户机的请求。

常用负载均衡技术

基于DNS的负载均衡

通过DNS服务中的随机名字解析来实现负载均衡,在DNS服务器中,可以为多个不同的地址配置一个名字,而最终查询这个名字的客户机将在解析这个名字时得到其中一个地址。因此,对于同一个名字,不同的客户机会得到不同的地址,他们也就访问不同地址上的Web服务器,从而达到负载均衡的目的。

反向代理负载均衡

使用代理服务器可以将请求转发给内部的Web服务器,让代理服务器将请求均匀地转发给多台内部Web服务器之一上,从而达到负载均衡的目的。这种代理方式与普通的代理方式有所不同。

正向代理

正向代理是指客户端经过一些配置后,客户端向代理服务器发送一个请求,并且指定目标服务器,之后代理向目标服务器转交并且将获得的内容返回给客户端。常见场景如翻墙。

反向代理

对于客户端来说,反向代理就好像目标服务器。并且客户端不需要进行任何设置。客户端向反向代理发送请求,接着反向代理判断请求走向何处,并将请求转交给客户端,使得这些内容好像就是它自己的一样。一次客户端冰不会感知到反向代理后面的服务,也因此不需要客户端做任何设置,只需要把反向代理服务器当成真正的服务器就好了。

基于NAT(Network Address Translation)的负载均衡技术

私有(保留)地址的”内部”网络通过路由器发送数据包时,私有地址被转换成合法的IP地址,一个局域网只需使用少量的IP地址(甚至是1个)即可实现私有地址网络内所有计算机与Internet的通信需求。

集群演进

直接分发多台单机应用

直接将单机应用部署到多态服务器上,通过Nginx进行分发是多数人首先想到的方式。这样的方式会遇到一些问题:

  1. Session登录信息共享问题

  2. 服务器定时任务并发的问题

ip hash policy

采用 Nginx中的 ip hash policy,对请求的ip地址进行哈希处理,同一个ip只会被分配到同一台机器上。

  • 优点: 可以不改变现有的技术架构,直接实现横向扩展。

  • 缺点: 1. 服务器负载不均衡,完全依赖 ip hash的计算。

    2. 客户机ip 动态变化频繁的情况下,无法进行服务。
    

采用缓存服务器

这是企业中常用的解决方案,使用redis或memcache等nosql数据库,实现一个缓存session的服务器,当请求过来的时候,所有的Tomcat Server都统一往这个服务器里读取session信息。这是企业中比较常用的一种解决方案。

项目搭建[6]springboot引入拦截器和过滤器

Posted on 2019-09-26 | In 项目搭建

拦截器和过滤器

拦截器和过滤器可以在收到客户端数据或返回数据给客户端时做统一的处理。是面向切面的思想。

不同的是,拦截器是Spring提供的处理办法,而过滤器是Tomcat针对Servlet提供的方法。

springboot引入拦截器(Interceptor)

本例以拦截器处理注解为例:

创建注解LoginRequired

1
2
3
4
5
6
7
/**
* 在需要登录验证的Controller的方法上使用此注解
*/
@Target({ElementType.METHOD})// 可用在方法名上
@Retention(RetentionPolicy.RUNTIME)// 运行时有效
public @interface LoginRequired {
}

创建拦截器AuthorityIntercepter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AuthorityInterceptor extends HandlerInterceptorAdapter{

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
// ①:START 方法注解级拦截器
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 判断接口是否需要登录
LoginRequired methodAnnotation = method.getAnnotation(LoginRequired.class);
// 有 @LoginRequired 注解,需要认证
if (methodAnnotation != null) {
// 这写你拦截需要干的事儿,比如取缓存,SESSION,权限判断等
System.out.println("====================================");
return true;
}
return true;
}
}

将拦截器添加到配置中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
registry.addInterceptor(AuthorityInterceptor()).addPathPatterns("/**");
}
@Bean
public LoginInterceptor LoginInterceptor() {
return new LoginInterceptor();
}

@Bean
public AuthorityInterceptor AuthorityInterceptor() {
return new AuthorityInterceptor();

}

springBoot引入过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component
@WebFilter(urlPatterns="/**",filterName="loginFilter")
public class LoginFilter implements Filter{

//排除不拦截的url
private static final String[] excludePathPatterns = { "/stuInfo/"};

@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {

HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;

// 获取请求url地址,不拦截excludePathPatterns中的url
String url = req.getRequestURI();
if (Arrays.asList(excludePathPatterns).contains(url)) {
//放行,相当于第一种方法中LoginInterceptor返回值为true
chain.doFilter(request, response);
}
//业务代码
}

@Override
public void destroy() {
// TODO Auto-generated method stub
}

}

项目搭建[5]maven环境隔离

Posted on 2019-09-25 | In 项目搭建

maven环境隔离

在企业级开发中,一般至少有三套环境: 本地环境、测试环境、正式环境。不同环境的配置文件的内容不同,如数据库连接,第三方接口等。
手动修改不仅耗时,而且容易出错。因此可以采用环境隔离。

操作步骤

resources

在 pom.xml文件的build节点下新增resources节点:

1
2
3
4
5
6
7
8
9
10
11
<resources>
<resource>
<directory>src/main/resources.${deploy.type}</directory>
<excludes>
<exclude>*.jsp</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>

profiles

project(build同级)下新增profiles节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>beta</id>
<properties>
<deploy.type>test</deploy.type>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<deploy.type>prod</deploy.type>
</properties>
</profile>
</profiles>

创建文件夹

在resources同级目录下创建三个文件夹:

  • resources.dev

  • resources.beta

  • resources.prod

将相同的配置留在原来的resources目录下,将不同的配置放在不同的目录下。

不同配置的打包

在mvn指令后增加 -PXXX就可以实现对应环境的打包,如生产环境的打包指令如下:

1
mvn clean install -Dmaven.test.skip=true -Pprod

项目搭建[4]lombok的使用

Posted on 2019-09-19 | In 项目搭建

Lombok简介

Lombok可以通过添加注解简化代码。Lombok主要采用 JSR 269 技术,在编译时增加相应的字节码信息。

注解处理技术简介

运行时解析

运行时能够解析的注解,必须将@Retention设置为RUNTIME,这样就可以通过反射拿到该注解。

java.lang.reflect反射包中提供了一个接口AnnotatedElement,该接口定义了获取注解信息的几个方法,Class、Constructor、Field、Method、Package等都实现了该接口。

编译时解析

Annotation Processing Tool(apt)

apt自JDK1.5产生,JDK1.7已标记为过期,不推荐使用,JDK8中已彻底删除,自JDK1.6开始,可以使用Pluggable Annotation Processing API来替换它,apt被替换主要有两点原因:

  • api 都在com.sun.mirror非标准包下

  • 没有继承到javac中,需要额外运行

Pluggable Annotation Processing API(JSR 269)

JSR 269自JDK6加入,作为apt的替代方案,它解决了apt的两个问题,javac在执行的时候会调用实现了该API的程序,这样就可以对编译器做一些增强,此时javac执行过程如下:

1
2
3
.java文件 ---> Parse and Enter ---> Annotation Processing ---> Analyse and Generate ---> .class文件
^ |
|________________________________________________+

Lombok使用 JSR 269

Lombok本质上就是一个实现了”JSR 269 API”的程序。在使用javac的过程中,它产生作用的具体过程如下:

  1. javac对源代码进行分析,生成了一棵抽象语法树(AST)

  2. 运行过程中调用实现了”JSR 269 API”的Lombok程序

  3. 此时Lombok就对第一步得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点

  4. javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)

Lombok常用注解

@Data

@Data注解在类上,会为类的所有属性自动生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。

1
2
@Data
public class Test{}

@Setter/@Getter

注解在属性上,为单个属性提供set/get方法;注解在类上,为该类所有的属性提供set/get方法,都提供默认构造方法。

可以增加value属性,通过AccessLevel限制该方法的作用域。

@Log4j

注解在类上;为类提供一个 属性名为 log 的 log4j 日志对象,提供默认构造方法。

@NoArgsConstructor/@AllArgsConstructor

@NoArgsConstructor: 注解在类上;为类提供一个无参的构造方法。

@AllArgsConstructor: 注解在类上;为类提供一个全参的构造方法,加了这个注解后,类中不提供默认构造方法了。

@EqualsAndHashCode

注解在类上, 可以生成 equals、canEqual、hashCode 方法。

@ToString

这个注解用在类上,可以生成所有参数的 toString 方法,还会生成默认的构造方法。

@Cleanup

这个注解用在变量前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的 close() 方法,如果该资源有其它关闭方法,可使用 @Cleanup(“methodName”) 来指定要调用的方法,也会生成默认的构造方法

@NonNull

注解在属性上,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常,也会有一个默认的无参构造方法。

@Log4j/@Slf4j

根据使用框架的不同,选用不同的注解。Lombok会自动为方法提供logger对象的创建语句。

计算机组成原理[1]二进制

Posted on 2019-09-09 | In 计算机组成原理

二进制

我们常用的计数方式是十进制,这可能来源于我们拥有10根手指。在计算机界,由于电子管只有开和关两种状态,用以表示十进制比较复杂。于是采用二进制进行计算。

二进制遵循逢二进一的原理,每一位只有 0 和 1 两个数字,这样的一位称为 binary digit (二进制位), 缩写为 bit(比特)。是计算机的最小单位。

由于二进制每一位包含的信息较少,所以二进制只能使用更多位数来表示其他进制的信息。

比如二进制的10表示十进制的2,二进制的100表示十进制的4.

二进制与十进制正整数的转换

由于二进制和十进制不存在倍数关系,所以转换时需要一定的计算。

二进制转十进制

任何进制都遵循以下规律:

x进制在y位上数值为z,z占权重为:

1
z * x ^ (y - 1)

而整个数字就是所有位的权重和。

比如十进制数345就是由以下公式计算而来:

1
3 * 10 ^ 2 + 4 * 10 ^ 1 + 5 * 10 ^ 0

同理,二进制数我们也可以使用按位计算并求和的方式转换为十进制。二进制有以下特点:

  1. 进制x = 2

  2. z等于1或者0 (为0时可以忽略不计,为1时权重等于 2 ^ (y - 1))

所以二进制数1001011转换为10进制的计算如下:

1
2 ^ 6 + 2 ^ 3 + 2 ^ 1 + 2 ^ 0

最终得到: 75

十进制转二进制

十进制转二进制使用如下计算方式:

1
不断将数字除以2,若余数为0,则在高位添加0,若余数为1,则在高位添加1

这是由于,任何一个十进制的正整数都可以表示为多个底数为2,指数不同的幂的和:

1
2 ^ x + 2 ^ y + ... + 2 ^ z

在不断除以2的过程中,会将低位计算并剔除。

除到第2 ^ y 时,此时公式为

1
2 ^ x / 2 ^ y + ... + 2 ^ y / 2 ^ y

由于 2 ^ (x - y) 肯定为偶数, 而 2 ^ y / 2 ^ y 为1,此时,若存在 2 ^ y这个加数,则余数为1,不存在则余数为0。

同步与异步,阻塞与非阻塞

Posted on 2019-09-09 | In 多线程

同步与异步

同步与异步主要描述在单线程中,执行者是否具备主动通知功能:

同步: 调用者会等到方法调用返回后才能继续后面的行为

异步: 调用者不需要等到方法返回,方法执行完毕后会主动通知调用者

阻塞与非阻塞

阻塞与非阻塞主要关心调用者的多个线程是否可以同时执行

阻塞: 线程1和线程2不能同时进行

非阻塞: 线程1和线程2可以同时进行

示例

同步阻塞

小明把水壶放到火上,然后在那傻等水开

(客户端请求服务器,在服务器返回前,客户端不执行任何操作)

同步非阻塞

小明把水壶放到火上,然后去客厅看电视,时不时的去厨房看看水开没有

(客户端新开一个线程请求服务器,主线程执行其他操作,同时主线程对请求结果进行轮询,发现收到请求结果后,执行后续操作)

异步阻塞

小明把响水壶放到火上,然后在那傻等水开

(客户端请求服务器,在服务器返回前,客户端不执行任何操作,服务器返回时,通过回调通知客户端执行后续操作)

异步非阻塞

小明把响水水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去处理

(客户端新开一个线程请求服务器,主线程执行其他操作,服务器返回时,调用回调客户端的回调接口通知主线程执行特定操作)

12…5

Sean

46 posts
10 categories
18 tags
© 2019 Sean
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4