volatile

Java多线程编程(3)--线程安全性

核能气质少年 提交于 2020-01-05 03:31:10
一.线程安全性   一般而言,如果一个类在单线程环境下能够运作正常,并且在多线程环境下,在其使用方不必为其做任何改变的情况下也能运作正常,那么我们就称其是线程安全的。反之,如果一个类在单线程环境下运作正常而在多线程环境下则无法正常运作,那么这个类就是非线程安全的。因此, 一个类如果能够导致竞态,那么它就是非线程安全的;而一个类如果是线程安全的,那么它就不会导致竞态。下面是《Java并发编程实战》一书中给出的对于线程安全的定义: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。   使用一个类的时候我们必须先弄清楚这个类是否是线程安全的。因为这关系到我们如何正确使用这些类。Java标准库中的一些类如ArrayList、HashMap和SimpleDateFormat,都是非线程安全的,在多线程环境下直接使用它们可能导致一些非预期的结果,甚至是一些灾难性的结果。一般来说,Java标准库中的类在其API文档中会说明其是否是线程安全的(没有说明其是否是线程安全的,则可能是也可能不是线程安全的)。   从线程安全的定义上我们不难看出,如果一个线程安全的类在多线程环境下能够正常运作,那么它在单线程环境下也能正常运作。既然如此

volatile

谁都会走 提交于 2020-01-05 03:30:51
volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。如果volatile变量修饰符使用恰当的化,它比synchronized的使用和执行成本耕地,因为它不会引起线程上下文的切换和调度 volatile的定义和实现原理: Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的 volatile保证可见性: 1:将当前处理器缓存行的数据写回系统内存 Lock前缀指令导致在执行指令期间,声言处理器的LOCK#信号,在多处理器环境中,LOCK#信号确保在声言该信号期间,处理器可以独占任何共享内存。在部分处理器中,LOCK#信号一般不锁总线,而是锁缓存,锁总线开销的比较大。对于Intel1486和Pentium处理器,在锁操作时,总是在总线上声言LCOK#信号。 在P6和目前的处理器中,如果访问的内存区域已经缓存在处理器内部,则不会声言LOCK#信号。相反,它会锁定这块内存区域的缓存并回写到内存,使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁定”

java volatile 关键字

余生长醉 提交于 2020-01-05 03:29:46
volatile: http://www.cnblogs.com/dolphin0520/p/3920373.html java理论与实践:正确使用volatile变量: 这里包含几种使用volatile变量的例子 http://www.ibm.com/developerworks/cn/java/j-jtp06197.html 前面讲述了源于volatile关键字的一些使用,下面我们来探讨一下volatile到底如何保证可见性和禁止指令重排序的。   下面这段话摘自《深入理解Java虚拟机》:   “观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”   lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:   1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;   2)它会强制将对缓存的修改操作立即写入主存;   3)如果是写操作,它会导致其他CPU中对应的缓存行无效。 volatile 不是用来进行原子性操作的 ;可见性。 用volatile修饰的变量,线程在每次使用变量的时候,都会图区变量修改后的最终值。 对于volatile修饰的变量

Volatile关键字补充

旧城冷巷雨未停 提交于 2020-01-05 03:22:49
前言 之前转载的文章 volatile关键字解析 中详细分析了volatile相关的知识(写的非常非常好!),这里就两点详细说下。 Synchonized保证可见性(以println方法为例) 之前有一次和朋友聊天说起System.out的各种print方法,当时他说这些方法内部除了IO外还有些别的逻辑,但当时没有特别在意。看了上面的博客后才意识到println通过同步的方法保证了可见性。用代码来解释吧: public class AtomicIntegerTest { static volatile AtomicInteger x = new AtomicInteger(0); //1 //static AtomicInteger x = new AtomicInteger(0); //2 public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); service.execute(new TestThread1()); service.execute(new TestThread2()); service.shutdown(); } static class TestThread1 implements Runnable{ @Override

线程之-volatile

我只是一个虾纸丫 提交于 2020-01-05 03:22:11
线程作为java面试中必须要掌握的一环,volatile多少也会在面试中被问到,所以就需要好好研究下,以面对面试官的问题。 首先要清楚线程不安全是什么原因引起的,需要明白计算机的cpu执行每条指令时都需要从高速缓存(cache)获取数据,如果没有则从主存中获取。这个就是问题的关键所在,当在多线程环境下每个线程都会有自己的线程栈,它们所持有的资源不是线程共享的,如果多线程都执行了对同一个变量的修改,那么在线程把值写回主存前,另一线程也修改了旧值,就会出现结果不一致的问题。 知道java内存模型就能可以知道,堆和方法去存放了对象和常量,变量等,java程序执行在java栈,每个线程栈是独享线程栈资源,共享堆和方法区的数据的。这就和高速缓存和主存的区别一致了。 volatile 1.可见性 在多线程环境下,某个共享变量如果被其中一个线程给修改了,其他线程能够立即知道这个共享变量已经被修改了,当其他线程要读取这个变量的时候,最终会去内存中读取,而不是从自己的 工作空间 中读取。 2.缓存一致性 线程中的处理器会一直在总线上嗅探其内部缓存中的内存地址在其他处理器的操作情况,一旦嗅探到某处处理器打算修改其内存地址中的值,而该内存地址刚好也在自己的内部缓存中,那么处理器就会强制让自己对该缓存地址的无效。所以当该处理器要访问该数据的时候,由于发现自己缓存的数据无效了,就会去主存中访问。 3.

浅尝java内存模型(JMM)

情到浓时终转凉″ 提交于 2020-01-05 03:22:02
本文参考《深入理解虚拟机》一书 一.JMM中的主内存和工作内存 主内存:主要是存储线程要读取的数据,如实例字段、静态字段和数组元素,局部变量和形参不存储在主存。 工作内存:每一个线程都有自己的工作内存,工作内存存储从主存中copy的一份副本,线程只能操作工作内存中的数据,不能直接操作主存中的数据。不同线程之间也无法访问其它线程的工作内存变量线程间变量值传递需要通过主存来进。 内存间进行交互操作: 由上面的交互关系可知,关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完成: lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。 unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。 read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用 load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。 use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。 assign(赋值):作用于工作内存的变量

java并发-Volatile关键字

时光怂恿深爱的人放手 提交于 2020-01-05 03:21:53
Volatile关键字最大的特点是,在多线程中保持变量的可见性。这个可见性主要是指变量在主存中的可见性。因此,有必要了解java内存模型。 java内存模型 java中每个线程有一块工作内存区,存放着被所有线程共享的主内存中的变量的值的拷贝。一个线程可以执行的操作有:使用(use)、赋值(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)。 主内存可执行的操作有读、写、锁定、解锁,每个操作都是原子的。一个变量是java程序可以存取的一个地址,它可以是基本类型、引用类型变量等。保存在主内存区的变量可以被所有线程共享,但一个线程存取另一个线程的参数或者局部变量是不可能的。 其中:使用(use)、赋值(assign)、锁定(lock)、解锁(unlock),这些操作都是原子操作,但是主内存与线程之间的数据传递不具有原子性,例如:当数据从主存中到工作内存时,需要两个动作: 1:主存执行读(read)操作; 2:工作内存执行相应的load操作; 下边我们看看上述各个操作的含义: 1:use:把一个变量在线程工作内存的拷贝内容传送给线程执行引擎; 2:assign:把一个值从线程执行引擎传送给变量的线程工作内存; 3:read:把一个变量的主内存拷贝传送到线程的工作内存,以便线程的load操作进行; 4:load

Synchronized、Threadlocal、Volatile

孤人 提交于 2020-01-05 03:21:36
synchronized: synchronized叫做同步锁,操作起来方便,只需要在一个方法或把需要同步的代码块包装在它内部,那么这段代码就是同步的了,所有线程对这块区域的代码访问必须先持有锁才能进入,否则则拦截在外面等待正在持有锁的线程处理完毕再获取锁进入正因为它基于这种阻塞的策略,所以它的性能不太好,但是由于操作上的优势, 只需要简单的声明一下即可,而且被它声明的代码块也是具有操作的原子性。 threadlocal (本地单机) 用来提供线程内的局部变量,这样每个线程都自己管理自己的局部变量,别的线程操作的数据不会对我产生影响,互不影响 就是把变量分成很多个拷贝,每个线程拥有一个。这里没有所谓的最后的结果,每个线程单独操作自己的变量,和其他的变量没关系,互不干扰 ThreadLocalMap类的定义是在ThreadLocal类中,真正的引用却是在Thread类中。同时,ThreadLocalMap中用于存储数据的entry定义,它是一个Map,他的key是ThreadLocal实例对象。 1、JVM利用设置ThreadLocalMap的Key为弱引用,来避免内存泄露。 2、JVM利用调用remove、get、set方法的时候,回收弱引用 3、当ThreadLocal存储很多Key为null的Entry的时候,而不再去调用remove、get、set方法,那么将导致内存泄漏 4

very strange and severe multithread inconsistency problem c#

一曲冷凌霜 提交于 2020-01-05 03:08:29
问题 I have a very simple watchdog program with 2 threads. One thread is updating a long variable and the other thread reads the variable. and alert if it was more than X seconds from the last update. The problem is that sometimes (happens once a day more or less) the second thread reads a stale value of the variable. Sometimes it is stale value from 3 seconds ago (i.e. the first thread updated the long variable but after 3 seconds the other thread didn't get the new value) I am using lock, in

volatile关键字

故事扮演 提交于 2020-01-04 23:36:07
首先简单介绍一下编译器对代码优化的概念: 编译器优化 :在不影响程序结果的情况下,改变程序的执行顺序提高效率 优化级别有: O0 O1 O2 O3 优先级别越高,优化的越厉害 如何优化?在此介绍volatile,我们只谈优化的一个方式,就是将频繁使用的变量直接加载到离cpu很近的寄存器中。 我们先来看如下代码: #include <stdio.h> #include <stdlib.h> #include <signal.h> int flag=1; void Handler(int signo){ printf("signo=%d\n",signo); flag=0; } int main(){ signal(2,Handler); while(flag){} } 在不优化的情况下直接进行编译,我们可预见:程序运行起来,当给这个进程发送二号信号flag值才会变为0使循环结束程序运行结束。 但当用O2使编译器对这个代码进行优化时,就会发现按下ctrl+c发送2号信号时,循环依旧不会停止。这是为什么呢? 原来 : 在编译器在优化过程中,若编译器判定某个数据是一个比较高的开销,然后编译器没有检测到有代码修改这个数据,便会把频繁使用的数据放到了寄存器中(while循环频繁使用flag,Handle函数虽对他进行修改但是由内核调用的,编译器并不知道),编译器就可能作出了错误的判断