禅与计算机 禅与计算机
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

sharkchili

计算机禅修者
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 项目编排

  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
      • 写在文章开头
      • 详解位运算技巧
        • 移位运算实现高效计算
        • 按位与和移位运算结合的效果
        • 按位或高效统计
        • 逻辑或简化if-else
        • 逻辑与运算短路特性的运用
        • Netty中按位与实现复杂均衡算法
      • 小结
  • CI-CD

  • 架构设计

  • 监控方法论

  • 软件工程实践
  • 场景设计
sharkchili
2026-03-25
目录

从开源框架中学习那些实用的位运算技巧

# 写在文章开头

众所周知位运算执行效率高于常规运算,通过不同的位运算搭配可以让我们写出简洁高效的表达代码的语义,所以本文将基于java主流开源项目的源码中介绍一下各类运算技巧及其使用场景。

我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili,也欢迎您了解我的开源项目 mini-redis:https://github.com/shark-ctrl/mini-redis (opens new window)。

为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。

# 详解位运算技巧

# 移位运算实现高效计算

移位运算从工作的角度来看就是直接将所有位置的数字简单移动x位,这个x就是我们要移动的位数,假设我们二进制数2,对应二进制就是0010,通过移位运算<<1得到的结果就是4,不难看出移位运算移动x位,就相当于乘2x的数学运算,同理右移运算就相当于除法,这里就不多做赘述了:

这种运算技巧笔者在线程池配置中是比较常用,例如我们需要配置一个IO密集型任务的线程池,对应的线程池最大数一般都是配置CPU核心数的2倍,而不同的服务器的CPU核心数不同,我们一般都是通过获取核心数然后进行乘2,此时我们完全就可以通过左移运算符完成这种逻辑运算:

@Bean("ioThreadPool")
    public ThreadPoolExecutor ioThreadPool() {

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                Runtime.getRuntime().availableProcessors(),
                //通过位运算计算线程池最大数
                Runtime.getRuntime().availableProcessors()<<1,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(200),
                ThreadFactoryBuilder.create().setNamePrefix("worker-").build(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        return threadPoolExecutor;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 按位与和移位运算结合的效果

按位与运算只有两个bit都为1的情况下才为1,如下所示2和7的按位与运算,只有低2位的bit都是1,所以最终的与运算只保留这一位,最终的结果就是2:

这里笔者给出一个比较经典的例子,假设我们的功能支持在8点到10点才能运行,我们希望能够判断当前时间是否是工作时间,这时候我们可以使用3个字节的byte做到这一点,我们使用24 个bit,自左向右为0~23点。

假设当前时间是8点,那么我们就可以自作向右将第8个bit设置为1,按照也就是自右向左的第16位,由此得出左移的位置为16,即需要左移15位也就是<<15,由此得出不同时间点的移动位数为1<<(23-当前时间),于是就有了下面这样一个按位与和移位运算的综合示例:

最终我们就有了下面这一段代码:

public static void main(String[] args) {
        //时间二进制转整数
        int timeBit = Integer.parseInt("000000001111100000000000", 2);

        for (int i = 0; i < 23; i++) {
            //当前时间
            int curTimeBit = (1 << (23 - i));
            //按位与
            if ((curTimeBit & timeBit) > 0) {
                log.info("{}:位于工作时间", i);
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

对应输出结果如下:

22:37:34.599 [main] INFO com.sharkchili.Main - 8:位于工作时间
22:37:34.602 [main] INFO com.sharkchili.Main - 9:位于工作时间
22:37:34.602 [main] INFO com.sharkchili.Main - 10:位于工作时间
22:37:34.602 [main] INFO com.sharkchili.Main - 11:位于工作时间
22:37:34.602 [main] INFO com.sharkchili.Main - 12:位于工作时间
1
2
3
4
5

# 按位或高效统计

在二进制运算之中,按位或运算只要是1个bit上为1,那么运算结果就为1,如下图所示,可以看到2的二进制数和1的二进制数的按位或运算结果就是3

这一技巧在JDK6版本的FutureTask中的任务状态就有着非常经典的用法,当我们需要取消FutureTask执行时就会调用cancel方法,其内部的AQS队列会判断当前任务状态是否为运行完成RAN(值为2)或者已取消CANCELLED(值为4),如果符合其中一种情况则不允许取消任务。

按照我们常规的写法,最终的写法可能是这样:

//判断是否可以取消任务
private boolean ranOrCancelled(int state) {
			//如果状态为运行完成或者取消则返回false,不允许当前线程打断任务
            if (state == RAN || state == CANCELLED) {
                return true;
            }
            return false;
  }
1
2
3
4
5
6
7
8

但是Doug Lea不是这样做的,它用到了更加高效的位运算,接下来笔者就对此进行逐步推理:

  1. 由上文可知RAN的值为2,CANCELLED的值为4,对应的是二进制的低二位。
  2. 通过或运算,将这两个状态的值拼接起来,构成0110。
  3. 我们希望判断当前状态是否符合其中的一种情况,实际上就可以通过按位与运算,只要我们状态的二进制值和0110相与大于0就说明符合其中某种状态

于是最终的位运算代码就变成了这样,可以看到作者先通过按位与拼接所有要判断的状态值,然后通过按位与判断是否符合其中一种情况,如果不为0说明命令某种状态,返回true:

 private boolean ranOrCancelled(int state) {
            return (state & (RAN | CANCELLED)) != 0;
        }
1
2
3

# 逻辑或简化if-else

逻辑或运算也是一个比较常用的运算符,它的工作原理是碰到一个运算结果为true则直接短路,不再进行后续的逻辑判断。 即if(表达式1||表达式2),如果表达式1为true,那么直接返回不再进行表达式2的计算。反之如果表达式1为false,则继续进行表达式2的计算:

这其中最经典的使用场景就是ConcurrentSkipListMap的putIfAbsent逻辑,这个方法会判断当前key是否存在,如果不存在才进行put操作:

new ConcurrentSkipListMap<>().putIfAbsent("key", "value");
1

而putIfAbsent和put方法底层都服用了putVal方法,只不过putIfAbsent的onlyIfAbsent传的是true,而put是false,所以后者遇到相同的key会覆盖value。 于是当HashMap进行put操作时遇到相同key时,就会有这样的逻辑:

  1. 如果onlyIfAbsent为false,说明是put操作,直接通过CAS原子操作覆盖掉value,如果失败则继续循环进行CAS操作。
  2. 如果onlyIfAbsent为true,则说明则说明是putIfAbsent,不进行修改直接返回当前map中的value。

假设我们现在就需要编写这样一段逻辑,我们的写法可能是这样:

//如果设置空才允许插入则直接返回原值
                        if (onlyIfAbsent) {
                            @SuppressWarnings("unchecked") V vv = (V) v;
                            return vv;
                        } else if (n.casValue(v, value)) {//反之就是put逻辑,直接原子修改然后返回修改后的v的值
                            @SuppressWarnings("unchecked") V vv = (V) v;
                            return vv;
                        }
1
2
3
4
5
6
7
8

实际上这种看看第一段逻辑是否为true,若不为true则进行第二段的逻辑完全可以通过本小节所提及的逻辑或运算:

//如果onlyIfAbsent为true则说明只有key为空情况下才可赋值,不执行后续逻辑直接返回map中的vv
//反之进行原子操作然后将修改后的vv返回
 if (onlyIfAbsent || n.casValue(v, value)) {
         @SuppressWarnings("unchecked") V vv = (V)v;
           return vv;
}
                    
                    
1
2
3
4
5
6
7
8

同理我们再给出这样一个例子演示一下笔者的重构思路,在著名的java度量框架micrometer中有一个非常实用的timer计时器,其底层有个时间窗口工具,它会并发传入最新的样本值sample和我们当前维护的原子类max进行比对,对应逻辑为:

  1. 如果max大于sample,则直接返回
  2. 如果max小于sample,则通过cas的方式将其修改为sample,因为可能涉及并发更新原子类,所以失败后就必须重新拉取原子类的最新值进行比对,明确sample依然大于这个值才能进行更新。

于是我们就可以编写出下面这样一段代码:

private void updateMax(AtomicLong max, long sample) {
        long curMax = max.get();
        //传入sample小于当前维护的原子类直接返回
        if (curMax >= sample) {
            return;
        }
        //sample大于当前维护的max则不断cas,若失败则比对再进行cas
        for (; ; ) {
            //cas成功则直接返回
            if (max.compareAndSet(curMax, sample)) {
                break;
            }
            //cas失败则重新获取max,进行比对
            curMax = max.get();
            if (curMax >= sample) {
                break;
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

我们先来第一步重构吧,review一下这段代码,可以发现代码稍显啰嗦,循环上下都涉及sample的比对,稍加思索,我们就可以发现将循环内部的sample比对逻辑移除,同时直接将循环上方的比对逻辑直接放到循环内部。 这样一来比对逻辑和循环cas并发更新都共享一段比对逻辑:

 private void updateMax(AtomicLong max, long sample) {
        
        
        //sample大于当前维护的max则不断cas,若失败则比对再进行cas
        for (; ; ) {
            long curMax = max.get();
            //传入sample小于当前维护的原子类直接返回
            if (curMax >= sample) {
                break;
            }
            //cas成功则直接返回
            if (max.compareAndSet(curMax, sample)) {
                break;
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

结合这段逻辑,我们发现这段逻辑本质上就是:

  1. 比对sample小于max直接结束,反之进入步骤2
  2. cas更新,若成功则结束循环,反之继续循环回到步骤1

这本质上就是通过短路运算符精简这段逻辑,确保比对逻辑可以复用的情况下,通过短路运算符保证将两端逻辑合并到一个if分支上,以简化代码:

private void updateMax(AtomicLong max, long sample) {


        for (; ; ) {
            long curMax = max.get();
            //若sample小于max则直接短路结束循环,反之cas尝试更新max的值
            if (curMax >= sample || max.compareAndSet(curMax, sample)) {
                break;
            }

        }
    }
1
2
3
4
5
6
7
8
9
10
11
12

# 逻辑与运算短路特性的运用

和逻辑或有所不同,逻辑与运算必须两者都为true才能返回true,这意味着即使第一个表达式为true,第二个表达式依然会参与运算。反之如果第一个表达式为false,第二个表达式就没有计算的必要了:

这其中比较经典的用法就是FutureTask源码中cancel方法的运用,它的逻辑是FutureTask状态必须为NEW的情况下,才能进行CAS状态修改,按照常规的写法,我们可能会这样写:

//如果状态为new
        if (state==NEW){
            //使用CAS将状态修改为中断态,如果失败则返回false
            if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                    mayInterruptIfRunning ? INTERRUPTING : CANCELLED)){
                return false;
            }
        }
1
2
3
4
5
6
7
8

这明显这种if条件1为true,就执行条件2并查看条件2执行结果的逻辑进行后续逻辑处理的我们完全可以通过逻辑与运算替代:

 if (!(state == NEW &&
                UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                        mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
1
2
3
4

# Netty中按位与实现复杂均衡算法

为了保证线程的公平调度,我们通常会采用轮询算法,假设我们有8个线程,按照轮询算法一次调用顺序为:线程0、线程1 ........线程7,然后才从线程0开始:

按照正常的写法,我们可以通过原子类进行递增,然后和线程数进行取模运算即可获取到当前事件通过那个线程执行:

假设我们的计算的值来到16,按照取模算法它对应的索引为0,此时新来一个事件需要线程处理,于是原子类原子自增1得到17,通过取模运算最终得到索引1:

对应的源码实现实现如下:

//原子子类记录当前索引值
private final AtomicLong idx = new AtomicLong();

 @Override
        public EventExecutor next() {
        	//idx自增和线程长度进行取模
            return executors[(int) Math.abs(idx.getAndIncrement() % executors.length)];
        }
1
2
3
4
5
6
7
8

取模运算的性能表现明显是没有位运算出色的,所以Netty提出了一种更加高效的方案,即当线程数为2的次幂时,通过原子类自增和线程数-1进行按位与运算。 举个例子,假设当前索引值还是16,也就是的4次方,换算成2进制就是10000,然后2的次幂减去1即可得到小于2的次幂且低位全为1的二进制1111,这样一来我们idx无论怎么变化和这个减去1后的结果都会得到1个小于2的次幂,这个值永远小于等于线程索引。 所以索引16&15=0,当自增到17时得到的结果也很上文一样为1:

最终我们就得到了这样一个负载均衡算法:

 @Override
        public EventExecutor next() {
            return executors[idx.getAndIncrement() & executors.length - 1];
        }
1
2
3
4

# 小结

本文针对日常的如下几种情况:

  1. 逻辑判断
  2. 状态标识确认
  3. 多if-else分支

通过逻辑梳理和层层递进推导得出一条条简洁暴露的位运算表达式使得代码逻辑和执行更加高效且具备张力,希望对你有帮助。

我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili,也欢迎您了解我的开源项目 mini-redis:https://github.com/shark-ctrl/mini-redis (opens new window)。

为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。

编辑 (opens new window)
上次更新: 2026/03/26, 01:05:31
10亿数据高效插入MySQL最佳方案
基于NETAPP实现内网穿透

← 10亿数据高效插入MySQL最佳方案 基于NETAPP实现内网穿透→

最近更新
01
基于EasyExcel实现高效导出
03-25
02
浅谈分布式架构设计思想和常见优化手段
03-25
03
SpringBoot集成Prometheus与Grafana监控
03-25
更多文章>
Theme by Vdoing | Copyright © 2025-2026 Evan Xu | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×
×