“并发冲突” 分析与解决

并发冲突分析与解决

     我们都知道,我们计算机中的运算都是交给CPU处理的,如果对于一个单核CPU来说,在绝对的某个时刻内,CPU只能同时完成一个任务,不可能同时的处理多个任务。但是我们都知道,我们的计算机并不是只能同时运行一个程序,我们往往是多个应用一起运行,比如我们经常一边登着QQ,一边使用着浏览器,而且还一边听着音乐,而且在电脑的后台往往还运行着许多的程序,那我们的CPU是怎么处理这些任务的,让他们 “同时” 运行的呢?
     这里我们想要理解两个概念:并行并发

  • 并行:两个任务真正的同时进行,比如一个CPU是双核的,那么他就可以同时运行两个任务,我们也叫并行的运行两个任务。
  • 并发:两个任务看似是“同时”在发生,但是实际上是在交替进行,同一个时刻CPU实际上只能处理一个任务。

     我们现在的电脑的CPU基本上是多核的,比如一个八核的电脑,就可以并行的处理八个任务,但是我们知道我们电脑上的任务往往远远超过8个,比如图中,我的电脑现在的进程数就有112个,那每个进程的线程数肯定有更多。在这里插入图片描述
     CPU需要“同时”处理那么多的任务,就需要每个任务交替进行,这就叫做 “并发执行”。我们在启动了线程后,就会把线程的执行交给操作系统,操作系统会根据当前CPU的状态来安排不同的线程执行,CPU的将时间分为一个个的 时间片,每个线程都会分到相应的时间片,CPU会交替的处理各个任务,当时间片到了,就会转向另外的线程,这样在宏观的角度看上去,好像是多个任务 “同时” 在进行。这就是最基本的 并发执行

  • 高并发

     但是众所周知,现在我们的计算机都被互联网连接了起来,现在比如我们去访问一个网站(比如CSDN),我们的请求不再是被某台计算的CPU进行处理了,因为当流量达到一个很大的量级时,每时每刻都会用成百数千个请求去访问CSDN这个网站,这个时候的请求不是一般的个人服务器能解决的了,现在企业运用的处理技术基本都是 “分布式” 数据存储和计算,也就是我们平时经常听到的 “云计算”

     当我们的网站在一瞬间收到了几百万个请求时,我们的服务器会在一瞬间承受很大的负荷(比如双十一),这种情况就被我们称为 “高并发”也就是一瞬间服务器要并发的处理大量的请求任务。

     我们在日常的自行调试中基本上很难遇到 “高并发” 的情况,但是我们今天要讨论的 “并发冲突” 却是很容易遇到的,而且就算是 高并发 中也必须考虑并解决 并发冲突 这样一个很重要的问题,这样才能保证系统的正常运行。

  • 并发冲突

     “并发冲突” 这个问题其实非常好理解,其实就是这样的一个问题:比如你的银行里有100元钱,你想把钱取出来,你有两种方式可以取钱:去柜台手机取款。那我们可以考虑这样的一种情况,如果你找一个你的朋友用你的手机取钱,你去柜台取款。那因为系统处理数据总是需要时间,如果你的朋友可以在你去柜台取完100元钱的瞬间,就通过手机取款,这个是时候系统还在没有刷新,是不是你的朋友就又可以取出100元钱了呢?

     其实这就是 “并发冲突” 需要解决的问题,就是当两个线程同时需要处理同一个 共享数据 时,怎么样才能保证数据处理不出错误,这两个线程的处理怎样才能不互相冲突,当这一套逻辑运用到 “高并发” 的系统上时,就是需要解决,当很多请求同时出现,并且要对我的 “分布式” 数据系统进行访问或者修改时,我怎么才能保证处理的结果不出差错,符合预期。

     下面我通过具体的代码来模拟这样的情况:

import java.util.ArrayList;

public class Concurrency implements Runnable{
	//十个线程共同来操作count,让count--
    int count = 100;

    @Override
    public void run() {
            while (count > 0) {
                count--;
                System.out.println(Thread.currentThread().getName() + " count: " + count);
            }
    }

    public static void main(String[] args) {
    	//线程操作的共同对象
        Concurrency con =new Concurrency();
    
        //创建线程
        for(int i = 0; i < 10; i++) {
            Thread th1 = new Thread(con);
            th1.start();
        }
        }
}

     程序的运行结果如下:
在这里插入图片描述
     从结果我们可以看出,十个线程的启动并不是按照顺序的,而是我们 start() 以后,交给操作系统自行决定的,而且从结果我们也可以看出几个线程是交替运行的,而且我们可以看到 “98” 出现了两次,本来一个数据一直递减,同一个数字是不可能出现两次的,但是这里 “98” 出现了两次,说明 Thread-0Thread-1 都获取到了count = 98,这个数据,也就是 “同时取到了两份钱” !这就是多线程在处理同一个数据时,发生了 “并发冲突”

  • 解决方案

     那我们这里如何解决这个问题呢?解决这个问题的方法是多样的:

  • 我们可以给线程加 ,直接运用Java并发包中的锁类,比如 实现接口Lock,就能起到给线程加锁的作用:也就是同一个时间,只i能获得 的一个线程来处理 共享数据,其他线程必须进入阻塞态,等到该线程处理完成后,再来争夺锁的控制权。
  • 也可以利用 synchronized 包围的同步代码块来解决多线程之间的 并发冲突,这里我选择的就是这种方式,下面以具体代码为例:
    -import java.util.ArrayList;
public class Concurrency implements Runnable{
    int count = 100;
    
    @Override
    public void run() {
    /**
		与上面代码唯一的区别,用synchronized将每个线程都会调用的,发生			  	冲突的代码块包裹,这样同一个时间就只用一个线程能够运行该段代码。
	*/
        synchronized (this) {
            while (count > 0) {
                count--;
                System.out.println(Thread.currentThread().getName() + " count: " + count);
            }
        }
    }

    public static void main(String[] args) {
        Concurrency con =new Concurrency();
        //创建线程
        for(int i = 0; i < 10; i++) {
            Thread th1 = new Thread(con);
            th1.start();
        }
        }
}

加入同步代码块之后,运行的结果如下:
在这里插入图片描述
     可以看到当Thread-0这部分代码获取到同步代码块的控制权以后,其他线程进入阻塞状态,必须等Thread-0释放了对锁的控制权以后,其他的线程才能进入! 这样,就解决了多线程中 并发冲突 的问题!

     其实,在Java并发包的底层同样也是利用了synchronized关键字,只不过是锁类对其具体的代码做了进一步的封装,更加方便使用。

     当然解决 “并发冲突” 的方法有许多种,比如应用 阻塞队列 、或者是 生产消费者模型 等。不同的技术选择适用于不同的应用场景,大家应该根据具体的应用场景进行适当选择!

     以上属于个人观点,欢迎大家指正交流,一起进步!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值