Java并发编程中如何保证原子性

骑猪看日落 2021-12-22 12:15 583阅读 0赞

原子性,可见性,有序性是并发的三大特征,所谓原子性,就是一个操作要么全部执行,要么都不执行。

如下所示,在一个类中,定义一个静态变量int var=0,现在开启20个线程,每个线程都执行相同的操作,即对var实行10000次++操作,线程退出,然后打印var的值。

  1. package com.xxx.cas;
  2. public class PlusPlusOP {
  3. public static int var = 0;
  4. public static void plusAndPlus(){
  5. var++;
  6. }
  7. public static void main(String[] args) {
  8. Thread threads[] = new Thread[20];
  9. for(int i=0;i<20;i++){
  10. threads[i] = new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. for(int i=0;i<10000;i++){
  14. plusAndPlus();
  15. }
  16. }
  17. });
  18. threads[i].start();
  19. }
  20. while(Thread.activeCount()>1){
  21. Thread.yield();
  22. }
  23. System.out.println("var=>"+var);
  24. }
  25. }

默认,没有对原子操作做特殊的处理,我们的到的结果各种各样,但就不是200000。如下所示:

20190629170250728.png

这是一个很常见的并发问题,涉及到的是原子操作,var++这个动作,其实分为了三个过程:

  1. 读取var的值
  2. ++操作
  3. 写入var的值

多线程并发的情况下,有可能第一个线程获取了var=10000,即将执行++操作,这时候另一个线程也来获取var,这时候获取的应该也是10000,当他们都执行完成的时候,本来应该是10002,但是实际上,因为并发的原因,导致了最终的结果是10001。所以我们看到的结果是一个小于200000的数字,程序在这种情况下运行,得到这个结果是正常的。再次运行,可能还是一个小于200000的结果。

那么如何解决这个原子性的问题呢,其实就是要保证var++这个动作不能被拆开,一旦进入读取var的值,那么接下来只有执行了写入var的值之后,别的线程才可以运行读取var的值操作。这就需要var++这个操作是同步的,解决办法可以有如下三种:

  • synchronized同步代码块
  • cas原子类工具
  • lock锁机制

三种解决方案的具体实现:

synchronized同步代码块

2019062917482027.png

cas原子类工具

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70

lock锁

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaW5pZmk_size_16_color_FFFFFF_t_70 1

发表评论

表情:
评论列表 (有 0 条评论,583人围观)

还没有评论,来说两句吧...

相关阅读