C/C++教程

并发编程(1)_CAS

本文主要是介绍并发编程(1)_CAS,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

问题引出

当我们使用多线程计数操作的时候,我们使用如下代码

package com.mmall.example.count;

import javax.xml.bind.SchemaOutputResolver;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 测试多线程计数类
 */
public class Test0913 {
    //总访问量
    static int count = 0;

    //模拟访问的方法
    public static void request() throws InterruptedException {
        //模拟耗时5毫秒
        TimeUnit.MILLISECONDS.sleep(5);
        count++;
    }

    //并发程序
    public static void main(String[] args) throws InterruptedException {
        //计算耗时需要获取当前时间、运行后时间,计算他们之间的差值
        long startTime = System.currentTimeMillis();
        //设置线程个数
        int threadsize = 100;
        //创建一个栅栏
        CountDownLatch countDownLatch = new CountDownLatch(threadsize);


        for (int i = 0; i < threadsize; i++) {
            //创建线程
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    //模拟用户行为,每个用户访问十次网站

                    try {
                        for (int j = 0; j < 10; j++) {
                            request();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        countDownLatch.countDown();
                    }
                }
            });
            thread.start();
        }
        //只有await方法的值为0整个程序才会结束
        countDownLatch.await();
        //拿到程序结束的时间
        long endTime = System.currentTimeMillis();

        System.out.println(Thread.currentThread().getName() + ",耗时:" + (endTime - startTime) + ", count:" + count);
    }
}

但是从该类的输出结果我们发现了输出的计数值总是不确定的,并且计数值不是我们想要的。这是因为存在线程不同步问题

问题分析

这里问题出自request方法中的count++操作,因为count++语句实际上是需要三步来完成(和jvm有关)

//count操作分解
1. 获取count的值,记作A A = count
2. 将A值+1,得到B B = A+1
3. 将B值赋值给count

如果由a b 两个线程,同时执行count++,那么假如a和b同时执行上面的第一步部操作,最后执行完后a+1、b+1,虽然线程进行了两次加法,但是count相当于只加了一次。

在这里为了避免这种错误,我们希望在执行count++操作的时候,同一时刻只允许一个线程去进行这个操作,因此引出了的概念,在java中,我们利用synchronized关键字来实现对资源的加锁。
于是我们将代码改进如下

package com.mmall.example.count;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 测试多线程计数类
 * 对request方法添加了锁
 */
public class Test09131 {
    //总访问量
    static int count = 0;

    //模拟访问的方法
    public static synchronized void request() throws InterruptedException {
        //模拟耗时5毫秒
        TimeUnit.MILLISECONDS.sleep(5);
        count++;
    }

    //并发程序
    public static void main(String[] args) throws InterruptedException {
        //计算耗时需要获取当前时间、运行后时间,计算他们之间的差值
        long startTime = System.currentTimeMillis();
        //设置线程个数
        int threadsize = 100;
        //创建一个栅栏
        CountDownLatch countDownLatch = new CountDownLatch(threadsize);


        for (int i = 0; i < threadsize; i++) {
            //创建线程
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    //模拟用户行为,每个用户访问十次网站

                    try {
                        for (int j = 0; j < 10; j++) {
                            request();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        countDownLatch.countDown();
                    }
                }
            });
            thread.start();
        }
        //只有await方法的值为0整个程序才会结束
        countDownLatch.await();
        //拿到程序结束的时间
        long endTime = System.currentTimeMillis();

        System.out.println(Thread.currentThread().getName() + ",耗时:" + (endTime - startTime) + ", count:" + count);
    }
}

从这个程序的输出结果看

计数准确性问题确实解决了,但是出现了新问题,运行时间变得十分缓慢,这是因为我们的锁加在了整个方法上,导致执行整个request方法在同一时刻只能有一个线程在执行,实际上我们只需要对count++中的第三步操作加锁就可以,这里也引出了我们加锁的原则

在保证线程安全性的情况下,加锁的范围越小越好

这篇关于并发编程(1)_CAS的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!