&的妙用

    最近看Netty源码,看到了一段这样的代码:

private static boolean isPowerOfTwo(int val) {
        return (val & -val) == val;
}

一开始无法理解是什么意思,看方法名然后搜了一下,发现是用来判断一个数字是否为2的次方的,例如1,2,4,8,……这样的数。那为什么可以用来判断一个数是否为2的次方呢(要是我来写,估计写个循环然后不断乘2直到和给的入参相同return true,看上去写的弱爆了),明显他这个写法比较高端。所以来学习一下高端写法的原理。
    涉及到位运算,我们第一个想法是推出值的补码,因为计算机运算都是基于补码运算的。所以这里就有涉及到原码反码补码的概念了,大学一年级老掉牙的东西还是温习一下:一个有符号数字,最高位为符号位,0为正,1为负。正数的反码补码都等于原码,负数的反码=原码除符号位的每一位取反,负数的补码=反码+1,举个例子:

1 = [00000001]原=[00000001]反=[00000001]补
-1 =[10000001]原=[11111110]原=[11111111]补

正是得益于补码运算,才有了上面的代码,我们看1和-1会发现,-1的补码高位全部都是1,我们都知道:和1做&运算,等于本身,我们再举个例子:

[2]=[00000010]原=[00000010]反=[00000010]补
[-2]=[10000010]原=[11111101]反=[11111110]补
[4]=[00000100]原=[00000100]反=[00000100]补
[-4]=[10000100]原=[11111011]反=[11111100]补

故而有了代码(val & -val)==val为2的次方。 后来看了,网上还盛传另一种基于位运算高效判断2的次方的方法:

private static boolean isPowerOfTwo2(int val) {
        return (val & (val-1))==0;
}

通过和自身-1的数求与判断是否为2的次方,他的原理是,2的次方形如2,4,8这样的数,都是(000……00100……00)这样的,只有一个1,然后减一之后就变成了(0……0111……1)这样的了,然后一求与,所有位都是0,这一特性来判断是否为2的次方。对于这一特性的运用,在Netty源码中还看到在求余数中被使用,我们正常求余数都是通过%来求余数,但是如果是2的次方,求余数则可以利用减一之后,全是1的特性来进行求与:

//length为2的次方时,可以这样求余数
public EventExecutor next() {
    return children[childIndex.getAndIncrement() & children.length - 1];
}
//正常情况下求余数
public EventExecutor next() {
    return children[Math.abs(childIndex.getAndIncrement() % children.length)];
}

在Netty中还看到这样一段源码:

if ((runTasks & 0x3F) == 0) {
	  lastExecutionTime = ScheduledFutureTask.nanoTime();
	  if (lastExecutionTime >= deadline) {
	      break;
	  }
}

每64个任务数判断一次是否超时,(runTasks & 0x3F) == 0用来判断是否是第64个任务数,0x3F其实就是16进制的63,其实和上面求余数一个当立,runTask & (length-1),length=64,当64时,求余数为0,从而来判断是否为64个任务数。

发布了39 篇原创文章 · 获赞 9 · 访问量 1024

猜你喜欢

转载自blog.csdn.net/qq_30095631/article/details/103453658