分类目录归档:PHP

swoole笔记(草稿)

第一部分:服务器
一、结构流程图
二、什么是reactor
三、异步和阻塞
四、worker和task_worker
五、主要方法
1、主进程方法
swoole_server::__construct
swoole_server->set
swoole_server->start
swoole_server->reload
swoole_server->shutdown
swoole_server->stats

2、主进程和客户端之间
swoole_server->close
swoole_server->send
swoole_server->sendfile
swoole_server->sendto
swoole_server->sendwait

swoole_server->exist
swoole_server->connection_info
swoole_server->connection_list
swoole_server->heartbeat

3、worker之间通信
swoole_server->sendMessage
swoole_server->finish task_worker和worker之间通信

4、主进程和worker之间
swoole_server->bind
swoole_server->task
swoole_server->taskwait

5、定时器
swoole_server->tick
swoole_server->after
swoole_server->clearTimer

6、其他
swoole_set_process_name
swoole_get_mysqli_sock
swoole_version
swoole_strerror
swoole_errno
swoole_get_local_ip

六、回调函数
1、主进程内的回调函数:
onStart
onShutdown
onMasterConnect
onMasterClose
onTimer

2、管理进程内的回调函数

onManagerStart
onManagerStop

3、Worker进程内的回调函数

onWorkerStart
onWorkerStop
onConnect
onClose
onReceive
onTimer
onFinish

4、task_worker进程内的回调函数

onTask
onWorkerStart

第七:扩展服务器
1、HttpServer
2、WebSocket

第二部分:服务端
第三部分:进程管理

PHP中的子进程和消息队列

原文:http://blog.csdn.net/baidu_zhongce/article/details/49151689

本文将介绍PHP子进程的使用,使用linux消息队列机制来达成进程间的协作,最后用一个简单的例子来类比具体应用方案。

1. 子进程

1.1 创建子进程

int pcntl_fork ( void )

按照php官方的说明,pcntl_fork()函数会创建一个子进程,这个子进程仅PID(进程号) 和PPID(父进程号)与其父进程不同。成功时,在父进程执行线程内返回产生的子进程的PID,在子进程执行线程内返回0。失败时,在父进程上下文返回-1,不会创建子进程,并且会引发一个PHP错误。

fork之后,操作系统会复制一个与父进程近似的子进程,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,只有对于fork调用的返回会在父子进程中产生区别(见上)。简单理解就是fork之后,会产生一个新的子进程执行之后的代码。

至于哪一个进程最先运行,这与操作系统平台的调度算法有关,而且这个问题在实际应用中并不重要,对于父子进程协同运作,我们将介绍消息队列的使用。

1.2 结束子进程
当子进程处理完自己的任务,需要直接结束时,我们直接关闭该进程即可:

int posix_getpid ( void )

使用该函数,返回进程 id 号,为整型(integer)。在子进程中,该函数返回的即是子进程的进程号。

bool posix_kill ( int $pid , int $sig)

传递sig信号给pid对应的进程,关闭这个进程。sig参数可以百度“PHP信号管理”扩展阅读,这里我们使用简单的SIGTERM为终止进程用的软件终止信号。而pid使用刚刚介绍的获取进程id的函数即可获取。

2. 消息队列

2.1 创建消息队列

int ftok ( string $pathname , string $proj)

将一个可访问的文件路径名转换为一个可供 shmop_open() (此函数用于生产共享内存空间,即我们的消息队列)和其他系统VIPC keys使用的整数,proj参数必须是一个字符串,这个参数其实就是读写方式,一般使用a即可。

使用该函数,我们就可以简单生成一个消息队列用的键值。

resource msg_get_queue ( int $key [,int $perms = 0666 ] )

msg_get_queue()会根据传入的键值(即使用上一个函数获取的)返回一个消息队列的引用。如果linux系统中没有消息队列与键值对应,msg_get_queue()将会创建一个新的消息队列。
函数的第二个参数需要传入一个int值,作为新创建的消息队列的权限值,默认为0666。这个权限值与linux命令chmod中使用的数值是一致的。

该函数的返回值是一个用于访问消息队列的句柄。

2.2 发送消息

bool msg_send ( resource $queue , int $msgtype , mixed $message [, bool $serialize [, bool$blocking [, int &$errorcode ]]] ) 

queue对应消息队列的句柄。
msgtype涉及到接收消息时候的操作,如果msgtype=0,接收消息队列的第一个消息;大于0接收队列中消息类型等于这个值的第一个消息;小于0接收消息队列中小于或者等于msgtype绝对值的所有消息中的最小一个消息。最后的message是传递的消息。
serialize参数来指定是否需要将消息变量进行序列化,序列化后我们就可以存放结构更为复杂的变量。
blocking参数用来指定在队列满后msg_send的动作,当blocking为true时,msg_send会挂起,等待消息队列中有足够的空间放置消息时再次发送消息。而如果blocking为false则在发现队列满后不执行send直接结束。
errorcode 使用引用方式获取错误代码。

2.3 接受消息

bool msg_receive ( resource $queue , int $desiredmsgtype ,int &$msgtype , int $ maxsize, mixed &$message [, bool $unserialize =true [, int $flags = 0 [, int &$errorcode ]]] )

同样是非常长的一个函数,参数涉及到:
queue对应消息队列的句柄。

desiredmsgtype定义和发送用的msytype类似,只是0表示消息队列中的首个消息会被接受。

msgtype接受到消息的类型

maxsize接受消息的最大长度,如果消息长度超过这个限制,那么获取消息就会失败。

message消息本身,使用引用的方式获取值。

unserialize为true的情况下,不会对收到的信息进行二值化,也就是可以收到其他php脚本发来的数组或是复杂结构;为false的情况下,返回的将是字符串。

Flags函数的处理方式:
MSG_IPC_NOWAIT,直接返回,如果失败则返回一个MSG_ENOMSG的整数。
MSG_EXCEPT,使用后当desiredmsgtype大于0 时,可获取第一条不为desiredmsgtype的消息。
MSG_NOERROR如果消息超过了最大长度,会进行截断并不返回错误。

3. 实例
介绍了这么多函数,其实根本就不知道该怎么用,这里设计了一个比较贴近生产环境的流程:

在父进程中,我们创建了5个子进程来一起处理任务,例子中是简单的echo,实际中可能是统计某日的数据,或者是计算用户答案是否正确。之后每个子进程会不断读取消息队列中的消息,在处理到一定数量的消息后,结束这个子进程。而父进程在创建完子进程后,只需要不断的在消息队列中写入需要处理的任务即可。

< ?php
$message_queue_key= ftok(__FILE__, 'a');
$message_queue= msg_get_queue($message_queue_key, 0666);

$pids= array();
for( $i = 0; $i &lt; 5; $i++) {
$pids[$i] = pcntl_fork();
if ($pids[$i]) {
echo "No.$i child process wascreated, the pid is $pids[$i]\r\n";
} elseif ($pids[$i] == 0) {
$pid = posix_getpid();
echo "process.$pidstart\r\n";
$count = 0;
do {
msg_receive($message_queue, 0,$message_type, 1024, $message, true, MSG_IPC_NOWAIT);
echo "process.$pid dealmessage{$message}\r\n";
$count++;
if($count == 5) {
break;
}
sleep(1);
} while (true);
echo "process.$pid end\r\n";
posix_kill($pid, SIGTERM);
}
}
for( $i = 0; $ i&lt; 25; $i++) { msg_send($message_queue, 1,rand(1000,10000)); } 
?>

Swoole框架之模型

一、
文件放置路径:{WEBPATH}/apps/models/
文件命名为:类名.php,首字母大写
必须继承自Swoole\Model,必须有一个公共属性$table,标识model对应的数据库表名。

namespace App\Model;
use Swoole;

class User extends Swoole\Model
{
    /**
     * 表名
     * @var string
     */
    public $table = 'users';
}

二、创建Model对象的方法:
1、Model函数
不存在创建,已存在获取

$object = model('UserInfo');
$object->get(10001);

相当于SQL语句的:

select * from user_info where id = 10001 limit 1

2、table函数
使用数据库表名来创建一个Model,这个函数与model不同,它不需要apps/models/目录中有对应的PHP文件。直接可生成Model对象。

$object = table('user_info');
$object->get(10001);

相当于SQL语句的:

select * from user_info where id = 10001 limit 1

三、内置方法:
1、model->get
从数据库中获取单条记录

function get($object_id=0,$where='')

参数为主键ID的值,生成的SQL类似这样

select * from table where id = {$id} limit 1

如果希望使用另外的字段进行查询,需要传入$where参数,如 $model->get(‘me’, ‘name’)。则生成的SQL为:

select * from table where name = 'me' limit 1

返回一个Record对象,在此对象上可以进行更多ORM操作。

2、model->set
修改单条数据库记录的内容

function set($id, $data, $where='')

$id 参数为主键ID,$where可以指定其他字段作为查询条件。
$data为修改的内容,必须为键值对应的数组,key将作为数据库字段名称,value为值。

3、model->del
删除单条数据库表记录。

function del($id, $where=null)

4、model->put
插入一条新的记录到表

function put($data)

$data 必须是键值(表的字段对应值)对应的数组

5、model->gets
获取表的一段数据,查询的参数由$params指定,传入第二个参数$pager,会自动构建分页器。

function gets($params, &$pager=null)

6、model->sets
更新一组数据,$data 更新的数据,$params update的参数列表

function sets($data, $params)

7、model->dels
删除一条数据包含多个参数

function dels($params)

8、model->all
获取到所有表记录的接口,通过这个接口可以访问到数据库的记录

function all()

9、model->count
返回符合条件的记录数
function count($params)

10、model->exists
检测是否存在数据,实际可以用count代替,0为false,>0为true

function exists($gets)

四、ORM

$content = $model->get(1);   //这里返回的是一个Record对象
$content->title = 'hello world';  //Update操作
$content->save();                 //保存操作,这时会执行SQL语句
echo $content->addtime;           //输入值
$content->delete();                 //删除此条数据

$all = $model->all();        //这里返回一个RecordSet对象
$all->filter('userid=2');    //增加一些限定条件,避免读取全部的数据库内容

//遍历符合条件的所有数据库记录
foreach($all as $obj)
{
    $obj->title = 'hello';
    echo $obj->content;
}

Swoole框架之数据库

一、配置:
修改 apps/configs/db.php ,加入配置。

$db['master'] = array(
    'type'    => Swoole\Database::TYPE_MYSQLi,
    'host'    => "127.0.0.1",
    'port'    => 3306,
    'dbms'    => 'mysql',
    'engine'  => 'MyISAM',
    'user'    => "root",
    'passwd'  => "root",
    'name'    => "db_live",
    'charset' => "utf8",
    'setname' => true,
    'persistent'  => true,
);
return $db;

$db下标表示不同数据库名,默认是master。

charset 制定数据库字符集
setname 在连接服务器成功后发送set names $charset
persistent 启用MySQL数据库长连接

二、使用方式

单数据库,默认读取master配置

$res = $this->db->query("select * from test")->fetchall();
var_dump($res);

多数据库,读取制定的数据库配置

$res = $this->db('other_db_config')->query("select * from test")->fetchall();
var_dump($res);

三、驱动类型

swoole框架支持3种驱动类型:

Swoole\Database::TYPE_MYSQL,使用mysql扩展
Swoole\Database::TYPE_MYSQLi,使用mysqli扩展
Swoole\Database::TYPE_PDO,使用PDO扩展

五、CURD操作
主要方法在Swoole/SelectDB.php中,这里大致介绍

function apt_test(){
     $apt = new Swoole\SelectDB($this->db);
     $apt->from('users');
      $apt->equal('id', 1);
      $res = $apt->getall();
     var_dump($res);
}

Swoole框架之组件

默认提供以下组件:
一、redis

二、mongo
原框架中有,这里已经被我停用

三、db
mysql、mysqli、pdo三种

四、ssdb
这个我自己加的,原框架没有

五、tpl
smarty模板

六:cache缓存
1、ApcCache
安装APC加速器后可以使用,apc缓存不适用于分布式环境

2、DBCache
数据库缓存

3、EAcceleratorCache
EAccelerator缓存,安装EAccelerator加速器后可以使用,EAccelerator缓存不适用于分布式环境

4、FileCache
文件缓存类,提供类似memcache的接口

5、Memcache
Memcache封装类,支持memcache和memcached两种扩展
memcached扩展采用libmemcache,支持更多特性,更标准通用
如需使用memcached,配置的时候加上:$config[‘use_memcached’]=true

6、Redis
使用Redis作为Cache

7、SaeMemcache
新浪的SAE Memcache

8、WinCache
WinCache缓存,安装WinCache加速器后可以使用
WinCache缓存不适用于分布式环境,且只能用于windows系统下

9、XCache
XCache缓存,安装XCache加速器后可以使用
XCache缓存不适用于分布式环境

七、event异步事件
1、当EVENT_MODE为空或者 ‘sync’ 提供同步事件
2、当EVENT_MODE为 ‘async’ 提供异步事件

八、log日志
1、DBLog
数据库日志记录类

2、EchoLog
直接输出日志到屏幕

3、FileLog
文件日志

4、PHPLog
使用PHP的error_log记录日志

九、kdb key-value数据库
Swoole Key Database,Swoole键值数据库
基于Cache和DB,实现了键值对应的自动缓存数据库,可用户,大量读,少量写的程序部分

十、upload 上传组件

十一、user 用户验证组件

十二、session

十三、http

十四、url curl组件

十五、limit
频率限制组件,用于计数和防爬虫

Swoole框架之命名空间

网站根目录定义为: {WEBPATH}

一、默认命名空间
1、顶层命名空间有2个
Swoole命名空间下的类是library类,对应目录是 {WEBPATH}/libs/Swoole/
App命名空间下是用户类,对应目录是{WEBPATH}/apps/classes/
顶层命名空间可以自己添加:

Swoole\Loader::addNameSpace($root, $path);

2、默认子命名空间
$root目录下新建子目录,目录内的php文件命名空间就是子命名空间
例如:
Swoole/Cache目录下Memcache.php文件命名空间就是:Swoole\Cache
App/Test目录下Go.php文件命名空间就是:App\Test

3、自动载入
swoole在启动的时候,会调用spl_autoload_register注册顶层命名空间到自动载入器

二、自定义命名空间

子命名空间直接声明在文件头,可以违背默认命名空间规则,无法被spl_autoload_register自动载入
App\Controller 是控制器类,对应文件放在{WEBPATH}/apps/controlers/
App\Model 是模型类,对应目录是{WEBPATH}/apps/models/

自定义命名空间载入方法需要另外写代码载入。
App\Controller载入在{WEBPATH}/libs/Swoole/Swoole.php中的runMVC()
App\Model载入在{WEBPATH}/libs/Swoole/ModelLoader.php中

Swoole框架之URL路由

swoole在处理路由的时候,依次调用2个路由钩子。
如果rewrite钩子有结果返回,则不调用mvc钩子
一、swoole_urlrouter_rewrite
这个函数会用到{WEBPATH}/apps/configs/rewrite.php中配置
1.正则配置。具体使用方法:

$rewrite[] = array(
    'regx' => '^/content/([a-z]+)\.html$',
    'mvc'  => array('controller' => 'content', 'view' => 'getlist'),
    'get'  => 'argv_1,argv_2',
);

regx需要传入一个正则表达式,符合该正则表达式就会进入此条URL路由
mvc指定对应的controller, view(也就是我们常说的action)对应方法
get可以将正则表达式中的子表达式(括号中的表达式)赋值到$_GET参数中

a.

$rewrite[] = array(
    'regx' => '^/hello/([a-z]+)\.html$',
    'mvc'  => array('controller' => 'content', 'view' => 'getlist'),
    'get'  => 'argv_1,argv_2',
);

比如/hello/index.html,会被解析成:
$_GET[‘argv_1′] = ‘hello′;
$_GET[‘argv_2′] = ‘index′;

并且调用{WEBPATH}/apps/controlers/Content.php中的function getlist()处理

2、系统自动处理:swoole_urlrouter_mvc
如果一条url经过第一步的处理,没有匹配到路由,则进入自动处理。
自动处理的步骤是,根据“/”分割,分割后第一个参数是controller,第二个参数是view
/hello/index/100,对应的controller为:hello,view为:index

剩下第三个参数处理规则,有2种:
a./hello/index/100 ,除去“/hello/index/”,最后一个参数是个数字的,自动赋值给 $_GET[‘id’] = 100
b./hello/index/cid-1-name-rango ,除去“/hello/index/”,后面的参数根据“-”两两分隔,自动赋值到 $_GET[‘cid’] = 1, $_GET[‘name’] = ‘rango’

PHP去数组空值

array_filter函数第二个参数没有加的时候,默认是去掉数组中为false的值。但是在php中0也被视为false,在需要保留的情况下就必须换个方法。

$filter = array(”,NULL,false);

$array=array_diff($array,$filter)

巧用array_diff把空值数组的差集计算出来

高效的MySQL分页

首先看一下分页的基本原理:

mysql> explain SELECT * FROM message ORDER BY id DESC LIMIT 10000, 20\G
***************** 1. row **************
id: 1
select_type: SIMPLE
table: message
type: index
possible_keys: NULL
key: PRIMARY
key_len: 4
ref: NULL
rows: 10020
Extra:
1 row in set (0.00 sec)

limit 10000,20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行,问题就在这里,如果是limit 100000,100,需要扫描100100行,在一个高并发的应用里,每次查询需要扫描超过10W行,性能肯定大打折扣。文中还提到limit n性能是没问题的,因为只扫描n行。

文中提到一种”clue”的做法,给翻页提供一些”线索”,比如还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,如果我们只提供”上一页”、”下一页”这样的跳转(不提供到第N页的跳转),那么在处理”上一页”的时候SQL语句可以是:

SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20;

处理”下一页”的时候SQL语句可以是:

SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 20; 不管翻多少页,每次查询只扫描20行。 缺点是只能提供”上一页”、”下一页”的链接形式,但是我们的产品经理非常喜欢”<上一页 1 2 3 4 5 6 7 8 9 下一页>”这样的链接方式,怎么办呢?

如果LIMIT m,n不可避免的话,要优化效率,只有尽可能的让m小一下,我们扩展前面的”clue”做法,还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,比如要跳到第8页,我看的SQL语句可以这样写:

SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20,20;

跳转到第13页:

SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 40,20; 原理还是一样,记录住当前页id的最大值和最小值,计算跳转页面和当前页相对偏移,由于页面相近,这个偏移量不会很大,这样的话m值相对较小,大大减少扫描的行数。其实传统的limit m,n,相对的偏移一直是第一页,这样的话越翻到后面,效率越差,而上面给出的方法就没有这样的问题。 注意SQL语句里面的ASC和DESC,如果是ASC取出来的结果,显示的时候记得倒置一下。 已在60W数据总量的表中测试,效果非常明显。 原文:http://ourmysql.com/archives/598 参考:http://www.percona.com/files/presentations/ppc2009/PPC2009_mysql_pagination.pdf

移位运算符

移位运算符
移位运算符就是在二进制的基础上对数字进行平移。按照平移的方向和填充数字的规则分为三种:< <(左移)、>>(带符号右移)和>>>(无符号右移)。
在移位运算时,byte、short和char类型移位后的结果会变成int类型,对于byte、short、char和int进行移位时,规定实际移 动的次数是移动次数和32的余数,也就是移位33次和移位1次得到的结果相同。移动long型的数值时,规定实际移动的次数是移动次数和64的余数,也就 是移动66次和移动2次得到的结果相同。
三种移位运算符的移动规则和使用如下所示:
< <运算规则:按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。 语法格式: 需要移位的数字 << 移位的次数 例如: 3 << 2,则是将数字3左移2位 计算过程: 3 << 2 首先把3转换为二进制数字0000 0000 0000 0000 0000 0000 0000 0011,然后把该数字高位(左侧)的两个零移出,其他的数字都朝左平移2位,最后在低位(右侧)的两个空位补零。则得到的最终结果是0000 0000 0000 0000 0000 0000 0000 1100,则转换为十进制是12.数学意义: 在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。 >>运算规则:按二进制形式把所有的数字向右移动对应巍峨位数,低位移出(舍弃),高位的空位补符号位,即正数补零,负数补1.
语法格式:
需要移位的数字 >> 移位的次数
例如11 >> 2,则是将数字11右移2位
计算过程:11的二进制形式为:0000 0000 0000 0000 0000 0000 0000 1011,然后把低位的最后两个数字移出,因为该数字是正数,所以在高位补零。则得到的最终结果是0000 0000 0000 0000 0000 0000 0000 0010.转换为十进制是3.数学意义:右移一位相当于除2,右移n位相当于除以2的n次方。
>>>运算规则:按二进制形式把所有的数字向右移动对应巍峨位数,低位移出(舍弃),高位的空位补零。对于正数来说和带符号右移相同,对于负数来说不同。
其他结构和>>相似。
小结
二进制运算符,包括位运算符和移位运算符,使程序员可以在二进制基础上操作数字,可以更有效的进行运算,并且可以以二进制的形式存储和转换数据,是实现网络协议解析以及加密等算法的基础。

移位运算
要点 1 它们都是双目运算符,两个运算分量都是整形,结果也是整形。
2 “< <" 左移:右边空出的位上补0,左边的位将从字头挤掉,其值相当于乘2。 3 ">>”右移:右边的位被挤掉。对于左边移出的空位,如果是正数则空位补0,若为负数,可能补0或补1,这取决于所用的计算机系统。
4 “>>>”运算符,右边的位被挤掉,对于左边移出的空位一概补上0。

位运算符的应用 (源操作数s 掩码mask)
(1) 按位与– &
1 清零特定位 (mask中特定位置0,其它位为1,s=s&mask)
2 取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)
(2) 按位或– |
常用来将源操作数某些位置1,其它位不变。 (mask中特定位置1,其它位为0 s=s|mask)
(3) 位异或– ^
1 使特定位的值取反 (mask中特定位置1,其它位为0 s=s^mask)
2 不引入第三变量,交换两个变量的值 (设 a=a1,b=b1)
目 标 操 作 操作后状态
a=a1^b1 a=a^b a=a1^b1,b=b1
b=a1^b1^b1 b=a^b a=a1^b1,b=a1
a=b1^a1^a1 a=a^b a=b1,b=a1

二进制补码运算公式:
-x = ~x + 1 = ~(x-1)
~x = -x-1
-(~x) = x+1
~(-x) = x-1
x+y = x – ~y – 1 = (x|y)+(x&y)
x-y = x + ~y + 1 = (x|~y)-(~x&y)
x^y = (x|y)-(x&y)
x|y = (x&~y)+y
x&y = (~x|y)-~x
x==y: ~(x-y|y-x)
x!=y: x-y|y-x
x< y: (x-y)^((x^y)&((x-y)^x)) x<=y: (x|~y)&((x^y)|~(y-x)) x< y: (~x&y)|((~x|y)&(x-y))//无符号x,y比较 x<=y: (~x|y)&((x^y)|~(y-x))//无符号x,y比较 应用举例 (1) 判断int型变量a是奇数还是偶数 a&1 = 0 偶数 a&1 = 1 奇数 (2) 取int型变量a的第k位 (k=0,1,2……sizeof(int)),即a>>k&1
(3) 将int型变量a的第k位清0,即a=a&~(1< >16-k (设sizeof(int)=16)
(6) int型变量a循环右移k次,即a=a>>k|a< <16-k (设sizeof(int)=16) (7)整数的平均值 对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,但是我们知道它们的平均值是肯定不会溢出的,我们用如下算法: int average(int x, int y) //返回X,Y 的平均值 { return (x&y)+((x^y)>>1);
}
(8)判断一个整数是不是2的幂,对于一个数 x >= 0,判断他是不是2的幂
boolean power2(int x)
{
return ((x&(x-1))==0)&&(x!=0);
}
(9)不用temp交换两个整数
void swap(int x , int y)
{
x ^= y;
y ^= x;
x ^= y;
}
(10)计算绝对值
int abs( int x )
{
int y ;
y = x >> 31 ;
return (x^y)-y ; //or: (x+y)^y
}
(11)取模运算转化成位运算 (在不产生溢出的情况下)
a % (2^n) 等价于 a & (2^n – 1)
(12)乘法运算转化成位运算 (在不产生溢出的情况下)
a * (2^n) 等价于 a< < n (13)除法运算转化成位运算 (在不产生溢出的情况下) a / (2^n) 等价于 a>> n
例: 12/8 == 12>>3
(14) a % 2 等价于 a & 1
(15) if (x == a) x= b;
   else x= a;
   等价于 x= a ^ b ^ x;
(16) x 的 相反数 表示为 (~x+1)

Java实例操作:
public class URShift {

public static void main(String[] args) {
int i = -1;
i >>>= 10;
//System.out.println(i);
mTest();
}

public static void mTest(){
//左移
int i = 12; //二进制为:0000000000000000000000000001100
i < <= 2; //i左移2位,把高位的两位数字(左侧开始)抛弃,低位的空位补0,二进制码就为0000000000000000000000000110000 System.out.println(i); //二进制110000值为48; System.out.println("
“);

//右移
i >>=2; //i右移2为,把低位的两个数字(右侧开始)抛弃,高位整数补0,负数补1,二进制码就为0000000000000000000000000001100
System.out.println(i); //二进制码为1100值为12
System.out.println(“
“);

//右移example
int j = 11;//二进制码为00000000000000000000000000001011
j >>= 2; //右移两位,抛弃最后两位,整数补0,二进制码为:00000000000000000000000000000010
System.out.println(j); //二进制码为10值为2
System.out.println(“
“);

byte k = -2; //转为int,二进制码为:0000000000000000000000000000010
k >>= 2; //右移2位,抛弃最后2位,负数补1,二进制吗为:11000000000000000000000000000
System.out.println(k); //二进制吗为11值为2
}

}
移位运算符面向的运算对象也是二进制的 “位”。 可单独用它们处理整数类型(主类型的一种)。左移位运算符(< <)能将运算符左边的运算对象向左移动运算符右侧指定的位数(在低位补0)。 “有符号”右移位运算符(>>)则将运算符左边的运算对象向右移动运算符右侧指定的位数。“有符号”右移位运算符使用了“符号扩展”:若值为正,则在高位插入0;若值为负,则在高位插入1。Java也添加了一种“无符号”右移位运算符(>>>),它使用了“零扩展”:无论正负,都在高位插入0。这一运算符是C或C++没有的。
若对char,byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int。只有右侧的5个低位才会用到。这样可防止我们在一个int数里移动不切实际的位数。若对一个long值进行处理,最后得到的结果也 是long。此时只会用到右侧的6个低位,防止移动超过long值里现成的位数。但在进行“无符号”右移位时,也可能遇到一个问题。若对byte或 short值进行右移位运算,得到的可能不是正确的结果(Java 1.0和Java 1.1特别突出)。它们会自动转换成int类型,并进行右移位。但“零扩展”不会发生,所以在那些情况下会得到-1的结果。