一个网站的建设步骤是,网络营销专业课程,网站建设内页,酒店网站的建设Java中的线程安全问题
谈到线程安全问题#xff0c;我们先说说什么是共享资源。所谓共享资源#xff0c;就是说该资源被多个线程所持有或者说多个线程都可以去访问该资源。
线程安全问题是指当多个线程同时读写一个共享资源并且没有任何同步措施时#xff0c;导致出现脏数…Java中的线程安全问题
谈到线程安全问题我们先说说什么是共享资源。所谓共享资源就是说该资源被多个线程所持有或者说多个线程都可以去访问该资源。
线程安全问题是指当多个线程同时读写一个共享资源并且没有任何同步措施时导致出现脏数据或者其他不可预见的结果的问题。 线程A和线程B可以同时操作主内存中的共享变量那么线程安全问题和共享资源之间是什么关系呢?
是不是说多个线程共享了资源当它们都去访问这个共享资源时就会产生线程安全问题呢?答案是否定的如果多个线程都只是读取共享资源而不去修改那么就不会存在线程安全问题只有当至少一个线程修改共享资源时才会存在线程安全问题。
最典型的就是计数器类的实现计数变量count本身是一个共享变量多个线程可以对其进行递增操作如果不使用同步措施由于递增操作是获取一计算一保存三步操作因此可能导致计数不准确。 假如当前count0,在t1时刻线程A读取count值到本地变量countA。
然后在t2时刻递增countA的值为1,同时线程B读取count的值0到本地变量countB,此时countB的值为0(因为countA的值还没有被写入主内存)。
在t3时刻线程A才把countA的值1写入主内存至此线程A一次计数完毕同时线程B递增CountB的值为1。
在t4时刻线程B把countB的值1写入内存至此线程B一次计数完毕。
这里先不考虑内存可见性问题明明是两次计数为何最后结果是1而不是2呢?其实这就是共享变量的线程安全问题。
那么如何来解决这个问题呢?这就需要在线程访问共享变量时进行适当的同步在Java中最常见的是使用关键字synchronized进行同步。
Java中共享变量的内存可见性问题
谈到内存可见性我们首先来看看在多线程下处理共享变量时Java的内存模型。 Java内存模型规定将所有的变量都存放在主内存中当线程使用变量时会把主内存里面的变量复制到自己的工作空间或者叫作工作内存线程读写变量时操作的是自己工作内存中的变量。
Java内存模型是一个抽象的概念那么在实际实现中线程的工作内存是什么呢? 一个双核CPU系统架构每个核有自己的控制器和运算器其中控制器包含一组寄存器和操作控制器运算器执行算术逻辑运算。
每个核都有自己的一级缓存在有些架构里面还有一个所有CPU都共享的二级缓存。那么Java内存模型里面的工作内存就对应这里的L1或者L2缓存或者CPU的寄存器。
当一个线程操作共享变量时它首先从主内存复制共享变量到自己的工作内存然后对工作内存里的变量进行处理处理完后将变量值更新到主内存。
那么假如线程A和线程B同时处理一个共享变量会出现什么情况?
假设线程A和线程B使用不同CPU执行并且当前两级Cache都为空那么这时候由于Cache的存在将会导致内存不可见问题具体看下面的分析。
线程A首先获取共享变量X的值由于两级Cache都没有命中所以加载主内存中X的值假如为0。然后把X0的值缓存到两级缓存线程A修改X的值为1,然后将其写入两级Cache,并且刷新到主内存。线程A操作完毕后线程A所在的CPU的两级Cache内和主内存里面的X的值都是1。线程B获取X的值首先一级缓存没有命中然后看二级缓存二级缓存命中了所以返回X1;到这里一切都是正常的因为这时候主内存中也是X1。然后线程B修改X的值为2,并将其存放到线程2所在的一级Cache和共享二级Cache中最后更新主内存中X的值为2。到这里一切都是好的。线程A这次又需要修改X的值获取时一级缓存命中并且X1,到这里问题就出现了明明线程B已经把X的值修改为了2,为何线程A获取的还是1呢?这就是共享变量的内存不可见问题也就是线程B写入的值对线程A不可见。
那么如何解决共享变量内存不可见问题?使用Java中的volatile关键字就可以解决这个问题。