什么是线程安全

1、开发版本

在PHP安装版本中存在两个版本,线程安全版本和线程非安全版本

下面是对开发版本的解释:

ts(Thread-Safety)即线程安全,多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染php以ISAPI方式加载的时候选择这个版本.,php以ISAPI方式加载的时候选择这个版本。
nts(None-Thread Safe)即非线程安全,就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的是 脏数据php以fast cgi方式运行的时候选择这个版本,具有更好的性能;

具体在哪里可以看呢,如下图打印phpinfo()会显示一些配置信息:
1590417033.png

好在我们在使用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;
    }
}

最后

那么问题来了,在上面超买超卖,使用了线程安全的写法实现后就不存在线程安全了吗?结果是即便是程序代码的实现了线程安全,商品的库存是存在在数据库的,两个安全的线程,读取数据库库存进行修改,也会有存在竞争关系。这个时候就用采用加锁的方式来实现了,参考小编其他文章。

阅读 197

推荐阅读