1、开发版本
在 PHP 安装版本中存在两个版本,线程安全版本和线程非安全版本
下面是对开发版本的解释:
ts(Thread-Safety) 即线程安全,多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染 php 以 ISAPI 方式加载的时候选择这个版本.,php 以 ISAPI 方式加载的时候选择这个版本。nts(None-Thread Safe) 即非线程安全,就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的是 脏数据 php 以 fast cgi 方式运行的时候选择这个版本,具有更好的性能;
具体在哪里可以看呢,如下图打印 phpinfo() 会显示一些配置信息:
好在我们在使用 PHP 版本的时候,不用可以去区分线程安全和非线程安全。ISAPI 方式加载的时候选择这,由微 软提出,故只能在 win 平台上运行。在 Linux 运行的都是线程非安全版本,不论在什么平台、用什么 web server,只要是用 cgi/fastcgi 方式运行 PHP,都用非线性安全。
产生线程不安全的原因
在单线程的开发过程中是不会存在线程安全问题的,只有在多线程的使用过程中,多个线程访问共享资源的时候,才会出现线程安全问题。只要资源没有发生变化, 多个线程读取相同的资源就是安全的。
结合一下超买超卖的应用场景。想象下线程 A 和 B 同时执行同一个商品下订单。
A:读取商品的库存为 1
B:读取商品的库存为 1
A:下单成功扣减库存 -1
B:下单成功扣减库存 -1
B:操作成功,库存减少为 -1
此时会发现,库存只有一件的商品,被卖出去了两次。出现了超卖的现象。结合线程产生不安全的原因分析:只要资源没有发生变化,线程就是安全的,现在访问的资源的库存发生了变化,导致线程变为了不安全。
常见的高并发导致的超卖超买的问题,用更专业的术语来说的话就是线程不安全。那么问题来了,怎么使线程变为安全呢?本文从代码层面来分析(其他层面分析请阅读小编其他文章)可以从资源不被多个线程共享和顺序执行(加锁)两个角度入手分析
局部变量
局部变量存储在线程自己的栈中。也就是说,局部变量永远也不会被多个线程共享。所以,基础类型的局部变量是线程安全的。下面是基础类型的局部变量的一个例子:
public void someMethod(){
$value = 0;
$value++;
}
判断资源对象是否是线程安全
如果一个资源的创建,使用,销毁都在同一个线程内完成,且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。
引用不是线程安全的!
重要的是要记住,即使一个对象是线程安全的不可变对象,指向这个对象的引用也可能不是线程安全的
class Calculator{
private $currentValue = null;
public function getValue(){return $this.currentValue;}
// 传入的参数的引用的变量, 将引用变量赋值给 currentValue,随着引用变量的改变,currentValue 的也会跟着改变
public function setValue(&$newValue){$this.currentValue = $newValue;}
}
最后
那么问题来了,在上面超买超卖,使用了线程安全的写法实现后就不存在线程安全了吗?结果是即便是程序代码的实现了线程安全,商品的库存是存在在数据库的,两个安全的线程,读取数据库库存进行修改,也会有存在竞争关系。这个时候就用采用加锁的方式来实现了,参考小编其他文章。