Java 位操作的高性能网站建设指南到底有多高

【技术水】java中的位运算与实际使用入门_java吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:681,541贴子:
【技术水】java中的位运算与实际使用入门收藏
什么是位运算众所周知,java是支持位运算的。&是按位与|是按位或通常情况下,多数人只在java基础入门的时候简单看过大概了解了一下这么个东西,知道有这么回事就接过去,也不知道这种东西在实际写代码中什么地方能够用到。位运算的运算规则如下1 & 1 = 11 & 0 = 00 & 1 = 00 & 0 = 01 | 1 = 11 | 0 = 10 | 1 = 10 | 0 = 0然而知道了这个又有什么用处呢?好,下面我来举个例子。----------------------------------------------------------------------------------------------------------------我们来设计个游戏的pojo众所周知,一个游戏的角色会有很多属性,比如唯一编号id,名字name,性别sex等等等。其中还有一些很重要的属性开关,比如这个角色是否可以被眩晕,是否可以被减速,是否可以被冰冻等等一系列状态位的开关。普通的做法是怎么样的呢?public class Status {
private boolean allowP//是否允许中毒
private boolean allowS//是否允许减速
private boolean allowF//是否允许冰冻
private boolean allowF//是否允许起火////get set省略}public class Boss extends Status {
public Boss(){
//Boss很厉害不吃任何状态
this.setAllowPoison(false);
this.setAllowSlow(false);
this.setAllowFreeze(false);
this.setAllowFire(false);
}}public class 杂兵 extends Status {
public 杂兵(){
//杂兵渣渣,什么状态都吃
this.setAllowPoison(true);
this.setAllowSlow(true);
this.setAllowFreeze(true);
this.setAllowFire(true);
}}----------------------------------------------------------------------------------------------------------------这么看着是不是特简单特直白,特别好操作呀。但是!有考虑过这个数据怎么保存在数据库里面吗?有考虑过状态扩展了怎么办吗?有考虑过这个角色被撸了一发之后判断是否能被冰冻/减速/中毒等需要调用多少方法吗?那有没有办法解决上面的问题还能看着简单粗暴呢?有,那就是使用位运算。----------------------------------------------------------------------------------------------------------------public class Status {
private static final int allowPoison = 1 && 0;//是否允许中毒
private static final int allowSlow = 1 && 1;//是否允许减速
private static final int allowFreeze = 1 && 2;//是否允许冰冻
private static final int allowFire = 1 && 3;//是否允许起火}咦?看上去好像没什么区别?我们接着写完public class Status {
public static final int allowPoison = 1 && 0;//是否允许中毒
public static final int allowSlow = 1 && 1;//是否允许减速
public static final int allowFreeze = 1 && 2;//是否允许冰冻
public static final int allowFire = 1 && 3;//是否允许起火//状态位
public void setFlag(int flag) {
this.flag =
public boolean isEnable(int status) {
return (flag & status) ==
public boolean isDisable(int status) {
return (flag & status) == 0;
public boolean isOnlyEnable(int status) {
return flag ==
}}public class Boss extends Status {
public Boss(){
//Boss很厉害不吃任何状态
this.setFlag(0);
}}public class 杂兵 extends Status {
public 杂兵(){
//杂兵渣渣,什么状态都吃
this.setFlag(15);
}}好,我们再回头看看之间提出来的问题。有考虑过这个数据怎么保存在数据库里面吗?有考虑过状态扩展了怎么办吗?有考虑过这个角色被撸了一发之后判断是否能被冰冻/减速/中毒等需要调用多少方法吗?一条一条的捋一遍两个版本的数据在数据库中的存储情况。oldVersion有多少个状态位就需要多少个字段newVersion一个int一共有32位,能保存32个状态。不超过32个状态的情况下只需要1个字段两个版本针对状态扩展情况oldVersion新增一个字段,需要新加一套get/set。数据库中新增一个字段。private boolean allowConfusion//是否允许混乱省略get/setnewVersion新增一个静态状态描述。public static final int allowConfusion = 1 && 4;//是否允许混乱两个版本针对多种状态(假定需要判断是否允许中毒/减速和冰冻)判断情况oldVersionif(boss.getAllowPoison() & boss.getAllowSlow() & boss.getAllowFreeze())newVersionif(boss.isEnable(Status.allowPoison | Status.allowSlow | Status.allowFreeze))以上就是个简单的位运算运用的例子
自学web前端,html5,css,js免费教程,web前端入门到精通,成为前端开发工程师...
接下来说一下可能的一些实际使用一个简单的权限系统的设计系统中一个用户通常会有以下几种权限select 查询update 更新insert 插入delete 删除对于不同的用户来说,这几种权限会有多种组合方式。最多会有16种组合方式,2的4次方。下面来上代码权限类public class Permission {
* 是否允许查询,0不允许,1允许
public static final int ALLOW_SELECT = 1 && 0;
* 是否允许插入,0不允许,1允许
public static final int ALLOW_INSERT = 1 && 1;
* 是否允许更新,0不允许,1允许
public static final int ALLOW_UPDATE = 1 && 2;
* 是否允许删除,0不允许,1允许
public static final int ALLOW_DELETE = 1 && 3;}顶层抽象用户类public abstract class BaseUser {
重新设置权限
public void setPermission(int permission) {
添加一项或多项权限
public void enable(int permission) {
删除一项或多项权限
public void disable(int permission) {
是否拥某些权限
public boolean isAllow(int permission) {
return (flag & permission) ==
是否禁用了某些权限
public boolean isNotAllow(int permission) {
return (flag & permission) == 0;
是否仅仅拥有某些权限
public boolean isOnlyAllow(int permission) {
return flag ==
}}整个设计只用了一个字段就表示了所有的权限的可能性。下面列举一部分权限组合状态下面举例说明一些常用操作新增一项修改权限enable(Permission.ALLOW_UPDATE);新增三项权限(新增,修改,删除)enable(Permission.ALLOW_INSERT | Permission.ALLOW_UPDATE | Permission.ALLOW_DELETE);判断是否允许查询isAllow(Permission.ALLOW_SELECT);判断是否允许查询和新增isAllow(Permission.ALLOW_SELECT | Permission.ALLOW_INSERT);判断是否只允许查询和新增isOnlyAllow(Permission.ALLOW_SELECT | Permission.ALLOW_INSERT);好,以上就是基于java位运算设计的一个简易的权限系统,代码简单易懂,扩展性可维护性可读性良好。
多年没上我要开启大召唤术了~
感谢大大分享,确实看到了特殊运算符的应用场景。接下来又是重构了
登录百度帐号推荐应用311被浏览139,752分享邀请回答4839 条评论分享收藏感谢收起1411 条评论分享收藏感谢收起Java的位操作
当读写二进制文件,或者要把非标准长度的整数与标准长度的整数互相转换时,就要用到大量的位操作,虽然看起来很简单,实际上里面却有很多细节很容易出错。
首先,有些标准跟C/C++是不同的:
1、Java采用高字节在前的方式读写数据,例如要把一个4字节的int数值写入文件时,它是按照从高字节到低字节的顺序写入的,读取的时候也是这样读出来。
而C/C++则采用平台相关的方式,在Windows平台采用低字节在前的方式,在/Unix平台则采用高字节在前的方式。
如果Java要读取C/C++创建的二进制文件,就要注意这个问题,最好先搞清楚原来的文件是采用哪种方式创建的。网络通信也要注意。
2、Java没有无符号数,无论byte,short,int,long都是有符号整数,而C/C++有个unsigned关键字可以设置一个数值为无符号数。
3、Java的整数基本数据类型就是byte,short,int,long这几个,长度分别为1,2,4,8字节,C/C++可以用typedef定义各种数据类型。
第二,Java是采用补码来存放整数的。
有时候觉得补码的定义有些奇怪,实际上可以这样理解:
把一个整数从0一直往上加1,加到溢出就变成了负数的最小值,然后再继续加1,最后又能回到0,实际上就是一个轮回。
例如一个byte类型的整数,一共有8位,能表示256个数值,采用补码的话数值范围就是-128~127,表示方法如下:
第三、不同长度的整数转换。
如果是从较短的数转成较长的数,很简单,如果是正数就在高字节补0,如果是负数就在高字节补1。
例如byte的127转为short的127:
short:11 0111
byte的-127转为short的-127
short:00 0001
如果是从较长的数转成较短的数,实际上就是把高位都截断了,所以转出来的数值可能完全不是一回事了。
例如short的256转为byte:
short:00 0000
把256变成了0
short的-255转成byte:
short:00 0001
把-255变成了1
第四、位运算操作符及它们的优先级
Java的位运算操作符包括:~非,|按位或,&按位与,^按位异或,&&左移,&&右移,&&&右移左侧补0
各种运算符的优先级如下表所示:&
! +(正) -(负) ~ ++ --
+(加) -(减)
& &= & &= instanceof
= += -= *= /= %= &= |= ^= ~= &&= &&= &&&=
根据该表可以看到,位运算操作符的优先级各有不同,分别为:
2、&& && &&&
另外需要特别注意的是,除了~,其他位运算操作的优先级都低于加减,所以要记得以下语句是返回32而不是7!
还有就是&、^、|的优先级都是低于逻辑操作符的,因此下面的语句会编译出错,幸好Java不像C那样对所有大于1的值都认为是真,否则下面的语句也能编译通过,但可能与你的意图不太一样,可能调试半天才发现。
如果记不清楚,还是按照你的意图加上括号最保险。
第五、字节数组与整数之间的转换
为了把一个整数存入文件,或者从文件中读取一个整数,需要经常在字节数组和整数之间转换,这个过程要用到大量的位运算。
首先需要记住的是,在参与所有运算前,Java都会把byte、short类型的值都转换成int,然后再对转换后的int进行操作。例如下面的语句会编译出错:
byte a=10,b=20,c;
因为a和b在相加前都被转成了int,最后得到的结果是个int类型的值,如果要赋给byte类型的c,必须显式地进行类型转换,即把第二句改为:
c=(byte)(a+b)
这一点很关键,因为对于一个最高位为1的byte类型的整数(负数),在运算之前它会被强制转换成int类型,根据上面所说的第三点,其实就是往前面的三个高字节补上1,这样一来,它在参与位运算的过程中,就不仅仅是它本身的8个bit参与了,实际上连前3个字节的24个bit(均为1)也参与了。例如有一个整数i=,它的二进制表示为:
分为4个字节存储,除了第一个字节是正数外,其余3个字节均为负数。假如用a代表最高字节的值,用b代表其他三个字节的值,如果按照通常的理解,你可能会这样得到i的值:
i=(a&&24)+(b&&16)+(b&&8)+b
如果a和b都是正数,上面的等式是成立的,但是在这个例子里,却是错的,因为上式中的a和b都已经被强制转换成了int类型再参加运算,实际上
i=00 00 11 00+11
最后得到的结果是,不是原来的值了。
为了不让byte在强制转换成int的过程加入了我们不想要的高位1,我们需要把它跟0xff进行与操作,i的值应该这样运算:
i = ( ( a& 0xff ) && 24 ) +( ( b & 0xff ) && 16 ) + ( ( b & 0xff ) && 8 ) + ( b & 0xff )
注意,因为&和&&的优先级都低于+,所以上面的括号是不能少的。不过由于跟0xff与操作之后,其余24位都变成了0,因此可以把+改为|操作,因为任何值与0进行或操作都得到本身:
i = ( a & 0xff ) && 24 | ( b & 0xff ) && 16 | ( b & 0xff ) && 8 | ( b & 0xff )
由于&&的优先级高于|,所以省了一些括号。最高字节可以不与0xff进行与操作,因为它转换成int后左边增加的3个字节都在左移24位时被去掉了:
i = a && 24 | ( b & 0xff ) && 16 | ( b & 0xff ) && 8 | ( b & 0xff )
把int转为字节数组的时候比较简单,直接右移截断即可:
byte[] b = new byte[4];
b[0] = (byte) (i && 24);
b[1] = (byte) (i && 16);
b[2] = (byte) (i && 8);
b[3] = (byte)
第六、非标准长度整数的存储和读取
假如有两个变量,他们的值可以用12个bit来表示,如果我们用16bit的short类型来表示一个变量,那么两个变量就需要4个字节,而实际上它们只需要3个字节就能表示出来,如果存储空间比较有限,写入文件时可以把它们存放在3个字节里面,但是读写过程就需要进行转换。
在内存里,它们都是标准的数据类型:
short a,b;
写入文件时,我们用第一个字节和第二个字节的前半部分来表示a,把第二个字节的后半部分和第三个字节来表示b,即:
1:xxxx xxxx
2:xxxx yyyy
3:yyyy yyyy
x和y都表示一个bit,分别用来存放a和b。写入时先把a和b转为字节数组:
byte[] out = new byte[3];
out[0] = (byte) ( a && 4 );//把a的高8位放在第一个字节
out[1] = (byte) ( a && 4 );//先把a左移四位,在右边补上4个0,第二个字节的高4位就是a的低4位了,第二个字节的高4位已经生成,低4位还是0
out[1] |= (byte) ( b && 8 & 0x0f );//b右移8位,并与0x0f进行与操作,实际上就只保留了b的高4位,并且是在字节的低4位上,跟第二步得到的字节进行或操作,就生成了第二个字节
out[2] = (byte)//把b的高4位截断就得到了低8位然后再把这个字节数组写入文件,就可以用3个字节表示两个整数了。
a =(short)( (out[0] & 0xff) && 4 | ( out[1] & 0xf0 )&&4);
b = (short)((out[1] & 0x0f) && 8 | ( out[2] & 0xff));打造高性能JAVA应用你需要知道的 - ImportNew
这篇文章节选自《java performance》,对java性能比较关心的同学大概都知道这本书,性能这个东西可能是很多同学在日常写java code的时候很少去关心的,但是在我们写code的过程中确又时时离不开对程序性能的影响,小到我们使用位运算来实现算术运算,大到我们对JAVA代码的总体架构设计,性能其实离我们很近。本片文章主要提到几个点,主要是在性能领域我们比较关注的一些问题,并且是有启发性的,如果同学对性能较感兴趣,那么我们可以一起深入研究各个点。
对于性能调优,通常会有三个步骤:1,性能监控;2,性能剖析;3,性能调优
我们对于操作系统的性能关注主要在下面几个点上:CPU利用率、CPU调度执行队列、内存利用率、网络I/O、磁盘I/O。
1,CPU利用率
对于一个应用来说,为了让应用达到最好的性能和可扩展性,我们不仅仅要充分利用CPU周期内可用的部分,而且要让这部分CPU的使用更有价值,而不是浪费。能够让CPU的周期利用的更充分对于多线程应用运行在多处理器和多核系统上至很有挑战性的。另外,当CPU达到饱和状态的时候并不能说明CPU的性能和伸缩性已经达到了最佳的状态。为了区分应用是如何利用CPU资源的,我们必须从操作系统级别来检测。在很多操作系统上,CPU的利用率统计报告通常包括用户和系统或内核对操作系统的使用。用户对CPU的使用是指应用用来执行应用代码执行所需要的时间。相比之下,内核和系统对CPU的使用是指应用用来执行操作系统内核代码锁花费的时间。高的内核或者系统CPU使用率可以表明共享资源紧迫,或者是有大量的I/O设备交互。理想的状态为了提高应用的性能和伸缩性,让内核或系统CPU时间为0%,因为花在执行内核或系统代码的时间是可以用来执行应用代码的。因此CPU使用优化的一个正确方向就是尽可能减少CPU花在执行内核代码或者系统代码上的时间。
对于计算密集型应用,性能监控比监测用户CPU使用和内核或系统CPU使用要更深层次,在计算密集型应用中,我们需要监测CPU时钟周期内的执行执行条数(Instructions per clock;IPC)或者是每条CPU执行所使用的CPU周期(cycles per instruction;CPI)。对于计算密集型应用来说我们从这两个维度来监测CPU是不错的选择,因为的打包CPU性能报告工具通常只会打印CPU的利用率,而不会打印CPU周期内CPU用来执行指令的时间。这意味着当CPU正在等待内存中的数据的时候,操作系统CPU性能报告工具也会认为CPU是正在使用的状态,我们把这个场景叫做“Stall”,“Stall”场景经常会发生,比如在CPU正在执行指令的任何时候,只要是指令需要的数据没有准备好,也就是没有在寄存器或者CPU缓存内,都会发生“Stall”场景。
当“Stall”场景发生的时候CPU会浪费时钟周期,因为CPU必须要等待指令需要的数据到达寄存器或者缓冲器。而且在这个场景中,数百个CPU时钟周期被浪费是很正常的事情,因此在计算密集型应用中,提高性能的策略是减少“Stall”场景的发生或者是增强CPU的缓存使用从而使得更少的CPU周期因为等待数据而浪费掉。这类的性能监控知识已经超越了本书的内容,需要性能专家的帮助了。然而,后面讲到的Oracle Solaris Studio Performance Analyzer这种性能剖析工具将会包括此类数据。
2,CPU调度队列
除了对CPU使用的监控,我们也可以通过监控CPU执行队列来检查系统是否已经满负载。执行队列是用来存储轻量级进程,这些进程通常是已经准备好执行了但是正在等待CPU调度而在调度队列等待的一种状态,当轻量级进程别当前处理器能来得及处理的数量更多的时候,调度队列将会产生。比较深的CPU调度队列表明系统已经满负荷了。系统的执行队列深度等于虚拟处理器执行不了的等待数,虚拟处理器数等于系统的硬件线程数。我们可以用java的api来拿到虚拟处理器数,Runtime.avaliableProcessors()。当执行队列深度大于虚拟处理器个数的四倍或更多的时候,操作系统将会出现反应迟钝的现象。
对于CPU调度队列的检测的一个通用指导是当我们发现队列深度高于虚拟进程数一倍的时候就要注意了,但是没有必要立即采取行动。当大于三倍或四倍或者更高的时候就要注意了,解决问题刻不容缓。
通常有两个可选的途径来观察队列的深度,第一个是通过增加CPU来分担负载或者减少对现有CPU的负载。这种途径从本质上减少了每个执行单元的负载线程数,从而减少执行执行队列的深度。
另外的一种途径是通过剖析系统运行的应用来增加CPU的使用率,换个说法就是寻找一种可以减少花费在垃圾回收上的CPU周期,或者寻找更好的算法来以更少的CPU周期来执行CPU指令。性能专家通常专注后面的一种途径:减少代码的执行路径长度和更好的CPU指令选择。JAVA程序员可以通过更好的执行算法和数据结构来提高代码的执行效率。
3,内存利用率
除了CPU的使用率,系统的内存属性也需要被监控,这些属性包括比如:分页、交换、锁、多线程引起的上下文交换等。
交换通常发生在当应用需要的内存大于实际的物理内存的时候,处理这种情况操作系统通常会配置一个相应的区域叫做交换区。交换区通常位于物理磁盘上,当物理内存内应用耗尽的时候,操作系统会将一部分内存数据暂时交换到磁盘空间上,这部分内存区域通常是访问频率最低的一块区域,而不会影响比较“忙”的内存区域;当被交换到磁盘区域的内存又被应用访问的时候,这个时候就需要从磁盘交换区将以页为单位读入内存,交换会影响应用的性能。
虚拟机的垃圾收集器在交换的时候性能非常差,因为垃圾收集器所访问的大部分区域都是不可达的,也就是垃圾收集器会引起交换活动的发生。场景是戏剧性的,如果垃圾收集的堆区域已经被交换到了磁盘空间,这个时候将会以页为单位发生交换,这样才能够被垃圾收集器所扫描到,在交换的过程中会戏剧性的引发垃圾收集器的收集时间延长,这个时候如果垃圾收集器是“Stop The World”(使得应用响应停止)的,那么这个时间就会被延长。
4,网络I/O
分布式JAVA应用的性能和伸缩性会受到网络带宽和网络性能的限制。例如,如果我们往网络接口发送比他能够处理的更多的数据包,数据包将会堆积在操作系统的缓冲区内,这将会引发应用延迟,另外其他的情况也会导致网络应用的延迟。
区分和监控的工具通常在操作系统的打包工具中很难找到。尽管linux提供了netstat命令,linux和solaris都提供了网络使用情况的实现,他们都提供了包括每秒发包、接包、错包、冲突等信息的统计。在以太网中,一小部分包冲突是很正常的现象。如果错包情况比较多那可能是网卡有问题了。同时,尽管netstat可以统计网络接口的发送和接收数据情况,这很难断定网卡是否被充分利用。例如,如果netstat -i显示现在每秒有2500个包从网卡发出,但是我们仍然无法判断当前的网络利用率是100%还是1%,我们仅仅能够知道目前有流量。这仅仅是在不知道网络包大小的情况下能够得到的结论。简单的说我们无法通过linux和solaris提供的netstat来判断当前网络是否影响了性能。我们需要一些其他的工具在我们的JAVA应用运行的过程中来监测网络。
5,磁盘I/O
如果应用有对磁盘进行操作,我们需要对磁盘进行监控,来监测可能出现的磁盘性能问题。一些应用是I/O密集型的,比如数据库。磁盘的使用通常还存在于应用日志系统,日志通常是我们用来记录系统运行过程中重要信息的。
可能感兴趣的文章
中文环境和英文环境不一样:LocalDateTime dt = LocalDateTime.pars...
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:ImportNew.
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2018 ImportNew

我要回帖

更多关于 高性能计算 的文章

 

随机推荐