zoukankan      html  css  js  c++  java
  • 线程安全性

    定义


      在看到线程安全时,我的第一反映就是函数的可重入性。但是这是在学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);
    }

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

  • 相关阅读:
    Flutter页面-基础Widget
    Data 方法、异常与类
    kafka手动设置offset
    centos 安装ftp服务BUG
    定时任务
    Java垃圾收集算法
    ByteBuffer数据结构
    HelloWorldDynamic
    HelloWorld
    sql技巧(增册改查)
  • 原文地址:https://www.cnblogs.com/xidongyu/p/5494605.html
Copyright © 2011-2022 走看看