Java实现多线程环境下的计数器功能

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/lynnlovemin/article/details/78050009

我们在做一个Web应用程序会遇到这样一个需求:在全局定义一个count,每次调用一个接口则count+1,用于统计接口调用次数和频率,代码入下:

public class CountServlet implements HttpServlet {

    private long count = 0;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        count++;
    }
}

上面这段代码如果在单线程环境没有问题,但是我们知道Servlet是多线程的,如果两个线程同时请求service,当线程1的count+1后,线程2进来了,这个时候count还是0,加1后得到的值还是1,这并不是我们想要的结果。有人会说在方法上面加上syncronized关键词就行了,这样做是没有问题,也能达到我们的要求。但是加上他后,线程1访问service方法后,会开启同步锁,线程2必须要等待线程1执行完成才能进行下一步操作,如果线程1执行太长或者死循环,会导致死锁,也大大影响了我们的程序性能。
在java1.5后,sun公司推出了java.util.concurrent同步包,我们可以使用这个包下面的一些类库来实现我们的需求,请看代码:

public class CountServlet implements HttpServlet {

    private AtomicLong count = new AtomicLong(0);

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        count.incrementAndGet();
    }
}

AtomicLong类保证了long类型的原子性,它和syncronized不同的是,他并不是简单加上同步锁,相反,他并不会锁住当前方法。而是利用一种称之为“无锁算法”的方式,而CAS(Compare And Swap)就是非常著名的无锁算法,它的基本原理大致是这样的:它包含3个操作数,内存值V、旧的值A,要修该的新的值B,仅当A和V相同时,将V修改为B,否则什么都不做。
请看具体的代码实现:

private void compareAndSwap(int v,int a,int b){
        if(v == a){
            v = b;
        }
        return v;
    }

在java里面,它是定义的native方法,通过native方法操作机器指令,从而实现数据的原子性。
但是如果有两个线程同时访问,会不会破坏其原子性,答案是当然不会。
因为它是由若干条指令构成的,用于完成一项功能,它是连续不断的,在执行过程中不允许被中断。
以上便是Atomic包下的类库的原理,在实现这类需求的时候就可以考虑通过它去实现,既保证了性能,又保证了原子性。

猜你喜欢

转载自blog.csdn.net/lynnlovemin/article/details/78050009