详解Java如何利用数字描述更多的信息

目录
  • 一 . 前言
  • 二 . 单数中描述信息
  • 三. 宏观思路
  • 总结

一 . 前言

这一篇来趣味性的探讨一下 , 如何通过更少的空间描述更多的信息

在数据库里面 ,通常我们会用数字的递进来描述状态等信息 , 但是如果想进行更复杂的操作 , 就有必要对二进制有一定理解了.

二 . 单数中描述信息

单数中保存多个信息的意思是 : 我们能把多少信息存储到一串数字里面. 这里直接来通过一些案例来说明用法

用单个数字来表示状态

这也是业务中最常见的一种使用方式 , 通过数字 1,2,3 等来描述一个状态 , 这种方式有一定的可读性 , 也有足够的扩展性

如果从二进制的角度说 , 这是一种进位体现状态的方式.

用单个数字来描述多个状态 : 包含多种状态

单数描述多个信息这一块首先能想到的就是 Linux 的权限表示法 , 在Linux 中有四种权限 , 分别如下 :

这种表示法的方式很简单, 从表象上来说就是两数相加 , 把初始状态设为 1 / 2 / 4 , 更复杂的状态则是初始状态的组合.

这种模式从二进制的角度看的话能更明显 , 每一位都标识一种状态.在更复杂的场景中, 还可以通过质数的数学特性 , 来做更多的扩展

用单个数字来描述不同维度的信息 : 包含状态和数量

这种体现最有代表的就是线程池的表现方式. 在线程池对象ThreadPoolExecutor 中 , 有个属性可秀了 , 它叫 ctl.

// ctl 是一个整形原子类 , 它即表示了状态 , 有记录了梳理
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
 //   : 此变量 记录了 “线程池中的任务数量”和“线程池的状态”两个信息
//    : 高3位表示"线程池状态",低29位表示"线程池中的任务数量"
 //       - RUNNING : 111 : 该线程池能接收新任务 ,且能对新任务进行处理
 //       - SHUTDOWN : 000 : 不能接收新任务 ,但是可以对任务进行处理
//        - STOP : 001 : 不添加新任务 , 不对任务进行处理 , 会中断正在执行的任务
 //       - TIDYING : 010 : 当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态
 //           - 当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态
//        - TERMINATED : 011 : 线程池彻底终止的状态

// 对数量的统计很简单 , 因为是 低 29 位用来描述梳理 , 所以做正常的加减即可实现
private boolean compareAndIncrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect + 1);
}

private boolean compareAndDecrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect - 1);
}

// 对状态的修改则是通过位运算来做的
> S1 : 预先准备多种状态
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

> S2 : 判断是否是运行状态或停止状态
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// ~CAPACITY将反转CAPACITY的值,也就是CAPACITY的高3位全部为1,低29位全部为0
// & 操作则可以得到高三位的值
private static int runStateOf(int c) { return c & ~CAPACITY; }
final boolean isRunningOrShutdown(boolean shutdownOK) {
    int rs = runStateOf(ctl.get());
    return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
}

这样一个数字则将数据进行了深度扩展 , 了解一定的二进制思想就足够了解这些逻辑的变化

三. 宏观思路

雪花算法对数字的整合

下面说的几种方式会跳出二进制 , 从业务的角度去看数字的玩法 , 先来看一段很常见的雪花算法

3 << 60 | timestamp - 1504000000000L << 20 | workerId << 10 | random.nextInt(128);

首位 :  可以自定义首位

时间戳 : 可以根据自己的业务情况定义毫秒级 (这里我随便给了个时间)

工作机器id:也被叫做workId,这个可以灵活配置,机房或者机器号组合都可以

随机序列号 : 自增值支持同一毫秒内同一个节点可以生成随机多少个ID

类似的方式还可以有很多 , 一个数有很长的位数 , 我们可以通过这些位数来实现不同的业务逻辑 , 以表达不同的涵义.

偏移量对进度的处理

说到偏移量 , 最简单的方向就是 for 循环时对 size 进行判断 ,当达到 size 后直接结束.

而稍微复杂点的就是 IO 流种对长度进行读取, 分批去读取数据.

更深度一点的就是即进行长度的扩展 , 又包含其他的信息 , 这一篇就来看一下 ObjectStream 中如何使用偏移量的.

ObjectStream 是一个对象流 , 通过 JDK 序列化对对象进行转换和解析.

在下面这个场景中 , 有一个对象叫 passHandle , ObjectStream 会通过该值作为下标获取存在对象列表中的对象 , 同时通过该对象判断整体的进度已经状态

// S1 : 初始化
private static final int NULL_HANDLE = -1;
private int passHandle = NULL_HANDLE;

// S2 : 写入 passHandler (PS : 此处是在 ObjectOutputStream 中)
 bout.writeInt(baseWireHandle + passHandle);

// S3 : 解析 passHandler
passHandle = bin.readInt() - baseWireHandle;
if (passHandle < 0 || passHandle >= handles.size()) {
    throw new StreamCorruptedException...
}

// S4 : 通过 Handler 取位数
Object lookupObject(int handle) {
    return (handle != NULL_HANDLE &&
            status[handle] != STATUS_EXCEPTION) ?
        entries[handle] : null;
}

除了作为偏移量来确定进度 , 还可以通过 passHandler 来标识状态

通常用偏移量来标识状态是需要和其他对象相配合的 , 例如集合 :

在下面这个案例里面 , passHandler 是作为 status 的下标存在的 , 而每个下标中都会存储一个状态, 则就对整个序列化中的状态进行了统计 :

private static final byte STATUS_OK = 1;
private static final byte STATUS_UNKNOWN = 2;
private static final byte STATUS_EXCEPTION = 3;

在这个案例里面 , 游标本身只代表了定位 , 但是它可以配合多个其他的对象 , 来扩展其含义. 同时游标可以通过设置负数形式 , 来扩展其状态含义. 当其等于 -1 时 , 则标识不存在该对象 , 是另外一种层面的扩展

总结

对数据的信息处理其实还有很多种方式 , 总结出来主要包括以下几种 :

  • 简单的通过其递增的变化 , 来对应不同的状态
  • 通过数字的叠加 , 来记录多种状态的叠加
  • 通过二进制内不同的位数 , 来记录多种层面的数据
  • 通过 long 等长位数 , 来为不同的位数匹配不同的含义
  • 通过作为一个游标 ,配合其他的对象做更深度的扩展

到此这篇关于详解Java如何利用数字描述更多的信息的文章就介绍到这了,更多相关Java描述信息内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Java如何利用数字描述更多的信息

    目录 一 . 前言 二 . 单数中描述信息 三. 宏观思路 总结 一 . 前言 这一篇来趣味性的探讨一下 , 如何通过更少的空间描述更多的信息 在数据库里面 ,通常我们会用数字的递进来描述状态等信息 , 但是如果想进行更复杂的操作 , 就有必要对二进制有一定理解了. 二 . 单数中描述信息 单数中保存多个信息的意思是 : 我们能把多少信息存储到一串数字里面. 这里直接来通过一些案例来说明用法 用单个数字来表示状态 这也是业务中最常见的一种使用方式 , 通过数字 1,2,3 等来描述一个状态 ,

  • 详解Java如何利用位操作符创建位掩码

    目录 位掩码 举个例子 编码 解码 提取一个比特 第一比特 任意位置的位 提取多个比特 应用一个比特掩码 总结 在本文中,我们来看看如何使用位操作符实现低级别的位掩码.我们将看到我们如何将一个单一的int变量作为一个单独的数据容器. 位掩码 位掩码允许我们在一个数字变量中存储多个值.我们不再把这个变量看作一个整数,而是把它的每一个比特当作一个独立的值. 因为一个比特可以等于 0 或 1,我们也可以把它看成是 false 或 true .我们也可以把一组比特切开,把它们当作一个较小的数字变量甚至是

  • 详解Java利用深度优先遍历解决迷宫问题

    目录 什么是深度优先 一个简单的例子 程序实现 什么是深度优先 什么是深度,即向下,深度优先,即向下优先,一口气走到底,走到底发现没路再往回走. 在算法实现上来讲,深度优先可以考虑是递归的代名词,深度优先搜索必然需要使用到递归的思路. 有的人可能会说了,我可以用栈来实现,以迭代的方式,那么问题来了,栈这种数据结构,同学们认为是否也囊括了递归呢?Java语言的方法区本身也是实现在一个栈空间上的. 一个简单的例子 我们以一个简单的迷宫为例,以1代表墙,0代表路径,我们构造一个具有出入口的迷宫. 1

  • 详解JAVA 内存管理

    前一段时间粗略看了一下<深入Java虚拟机 第二版>,可能是因为工作才一年的原因吧,看着十分的吃力.毕竟如果具体到细节的话,Java虚拟机涉及的内容太多了.可能再过一两年去看会合适一些吧. 不过看了一遍<深入Java虚拟机>再来理解Java内存管理会好很多.接下来一起学习下Java内存管理吧. 请注意上图的这个: 我们再来复习下进程与线程吧: 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调

  • 详解JAVA Spring 中的事件机制

    说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎么实现的呢?说listener之前,我们先从设计模式开始讲起. 观察者模式 观察者模式一般包含以下几个对象: Subject:被观察的对象.它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify().目标类可以是接口,也可以是抽象类或具体类. ConcreteSubject:具体的

  • 详解Java中的BigDecimal

    今天碰到一个问题,金额计算用double类型会丢失经度,就改用了BigDecimal类型,这个类型之前用的比较少,没怎么接触.就到网上看了一下相关教程,写个总结记一下. BigDecimal类 对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数的操作. BigDecimal构造方法 1.public BigDecimal(double val) 将double表示形式转换

  • 详解Java 中的 AutoCloseable 接口

    一.前言 最近用到了 JDK 7 中的新特性 try-with-resources 语法,感觉到代码相对简洁了很多,于是花了点时间详细学习了下,下面分享给大家我的学习成果. 二.简单了解并使用 try-with-resources语法比较容易使用,一般随便搜索看下示例代码就能用起来了.JDK 对这个语法的支持是为了更好的管理资源,准确说是资源的释放. 当一个资源类实现了该接口close方法,在使用try-with-resources语法创建的资源抛出异常后,JVM会自动调用close 方法进行资

  • 详解Java后端优雅验证参数合法性

    1.首先创建一个测试实体类Person,并携带如上注解,其注解的作用描述在message package com.clickpaas.pojo; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Range; impo

  • 详解Java分布式系统中一致性哈希算法

    业务场景 近年来B2C.O2O等商业概念的提出和移动端的发展,使得分布式系统流行了起来.分布式系统相对于单系统,解决了流量大.系统高可用和高容错等问题.功能强大也意味着实现起来需要更多技术的支持.例如系统访问层的负载均衡,缓存层的多实例主从复制备份,数据层的分库分表等. 我们以负载均衡为例,常见的负载均衡方法有很多,但是它们的优缺点也都很明显: 随机访问策略.系统随机访问,缺点:可能造成服务器负载压力不均衡,俗话讲就是撑的撑死,饿的饿死. 轮询策略.请求均匀分配,如果服务器有性能差异,则无法实现

  • 详解java中BigDecimal精度问题

    一.背景 在实际开发中,对于 不需要任何准确计算精度的属性可以直接使用float或double,但是如果需要精确计算结果,则必须使用BigDecimal,例如价格.质量. 为什么这么说,主要有两点 1.double计算会有精度丢失问题 2.在除法运算时,BigDecimal提供了丰富的取舍规则.(double虽然可以通过NumberFormat进行四舍五入,但是NumberFormat是线程不安全的) 对于精度问题我们可以看下实际的例子 public static void main(Strin

随机推荐