栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

线程安全问题原因及解决方法

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程安全问题原因及解决方法

首先,线程不安全会产生什么问题,我们看下面的代码作为例子

 class Count
{
    public int count=0;

     public void increase()
    {
        count++;
    }
}

public class Demo_eight {

    public static Count counter=new Count();

    public static void main(String[] args) throws InterruptedException {

        Thread t1=new Thread(()->{

            for (int i = 0; i <50000; i++) {
                counter.increase();
            }
        });

        Thread t2=new Thread(()->{

            for (int i = 0; i <50000; i++) {
                counter.increase();
            }

        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(counter.count);

    }
}

该代码的目的是使用两个线程同时调用class类的increase()方法实现对count进行自增,并将结果打印出来

按道理打印出来的结果应该是100000,但是结果却与我们预期的不同,这就是调度器的随机调度造成的线程不安全。

那么,为何调度器随机调度会造成线程不安全:

首先,count++这段代码是由三条机器语言执行的,分别是LOAD,ADD,SAVE,意思分别是从内存中读取到CPU,在CPU寄存器中完成加法运算,在把内容写回到内存中。程序运行一次count++的过程大概如下图所示:

此时来看是没什么问题的,但是由于调度器的随机调度,LOAD,ADD,SAVE 三条语句的顺序会发生变化,导致计算出错而达不到我们想要的结果。即可得知,线程不安全的其中一个主要原因是调度器的随机调度。

除此之外,线程不安全还有其它原因

1.调度器的随机调度(根本原因)。

2.多个线程同时修改一个变量。

3.操作不是原子的。

4.存在内存的可见性问题。

那么如何解决此类问题呢,我们这里可以使用两个方法:synchnized或者volatile方法

1.synchronized方法

该方法主要用于解决调度器的随机调度问题,用于给线程上锁,如下图所示,给increase()方法加上synchronized之后,若t1先拿到锁,t2就会阻塞等待,等到t1执行完,t2才能拿到锁,就解决了上述的调度器的随机调度问题:

class Count
{
    public int count=0;

    synchronized public void increase()
    {
        count++;
    }
}

2.volatile方法

volatile 方法主要是用来解决内存的可见性问题,那么首先,什么是内存可见性问题??

内存可见性问题主要是由于操作系统的自动优化造成的,如下图所示:

 LOAD进行的是对内存的读操作,但是如果每次读的内容都一样,JVM就会做出优化,省略了后面的LOAD操作,从而产生了内存可见性问题。使用volatile就可以阻止JVM进行这样的优化,从而解决线程安全问题。代码如下图所示:

static class Counter {
  public volatile int flag = 0;
}
public static void main(String[] args) {
  Counter counter = new Counter();
  Thread t1 = new Thread(() -> {
    while (counter.flag == 0) {
      System.out.println();
   }
    System.out.println("The circulation is over!");
 });
  Thread t2 = new Thread(() -> {
    Scanner scanner = new Scanner(System.in);
    System.out.println("Input a integer:");
    counter.flag = scanner.nextInt();
 });
  t1.start();
  t2.start();
}

这样就阻止了JVM对变量flag的优化。从而能使得循环结束。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1032373.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号