线程安全性

定义


  在看到线程安全时,我的第一反映就是函数的可重入性。但是这是在学C语言编程时提到的概念。而在面向对象编程中,我们就应该将焦点移到类上面来,即类的线程安全问题。那么什么样的类是线程安全的呢?通常可以这样来理解,当多个线程都要访问这个类时(这里写成对象更加具体),在主调代码中不需要加额外的同步/协同来控制对该类的访问,那么可以说这个类是线程安全的。

不安全诱因


  出现线程不安全的原因就是竞态条件的出现。当执行结果的正确性取决于线程执行的次序时,就会出现所谓的竞态条件。最常见的竞态条件就是“先检查后执行”操作,即if...then...语句。还有一种就是“读取-修改-写入”三者不连续的操作。

  竞态条件:计算结果的正确性取决于线程执行的次序。

  竞态条件将导致程序执行结果的不确定性,这在实际应用中是很危险的

无状态对象


  所谓的无状态表示他不包含任何域,也不包含任何对其他类中域的引用。简答的说就是它使用的总是局部变量。这样的话对变量的操作总是在自己的栈上实现的。下面看一个例子:

public class StatelessFactorizer implements Servlet{
    public void service(ServletRequest req, ServletResponse resp){
        BigInteger i  = extractFromRequest(req);
        BigInteger [] factors = factor(i);
        encodeIntoResponse(resq, factors);
    }
}

每个请求和应答会对应一个req和resp,并且service并没有引入其他域,所以该类是无状态的类,它是线程安全的。

加锁机制


  加锁可以让线程同步,对一些共享域进行串行访问,这让避免出现线程竞争的问题。但是有时加锁会让效率变得低下。

public class SynchronizedFactorizer implements Servlet{
    private BigInteger lastNumber;
    private BigInteger lastFactors;

    public synchronized void service(ServletRequest req, ServletResponse resp){
        BigInteger i = extractFromRequest(req);
        if(i.equals(lastNumber))
            encodeIntoResponse(resp, lastNumber);
        else{
            BigInteger[] factors = factors(i);
            lastNumber = i;
            lastFactors = factors;
            encodeIntoResponse(resp, factors);
        }
    }
}

  在servlet容器中,SynchronizedFactorizer对象只保存一份,每当一个请求来临时会开一个线程来执行service方法。上面的代码如果不加synchronized关键字则会出现竞态条件,但是加了之后则会降低服务的执行效率。

性能


  我们使用的并发的目的为了提高执行的效率,但是为了出现竞态条件或数据竞争等问题,往往必须引入原子类或者是锁机制,这样反而会降低性能。但是这个问题往往却是无法避免的。所以在开发中,我们应该采用折衷方式来考虑我们程序架构。

 

public void service(ServletRequest req, ServletResponse resp){
    BigInteger i = extractFromRequest(req);
    BigInteger[] factors = null;
    synchronized (this){
        ++hits; //共享域
        if(i.equals(lasNumber)){
            ++cacheHits;
            factors = lastFactors.clone();
        }
    }
    if(factors == null){
        factors = factor(i);
        synchronized(this){
            lasNumber = i;
            lastFactors = factors.clone();
        }
    }
    encodeIntoResponse(resp, factors);
}

 

在程序中我们只对涉及到共享域的部分进行了加锁操作,这样相对能提高执行的效率。

posted @ 2016-05-15 09:19  被罚站的树  阅读(262)  评论(0编辑  收藏  举报