123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- 关键字-volatile
- volatile是Java提供的一种轻量级的同步机制,Java中包含两种同步机制:
- |-同步方法、同步代码块 synchronized一般被称为重量级锁
- |-volatile变量,更轻量级,属于轻量级锁。因为它不会引起线程上下文的切换和调度。但是volatile变量的同步性比较差。也更容易出错。
- 作用:强制线程每次在使用的时候,都会看到共享区域的最新值
- 原子性:
- 原子性其实是Java并发变成的多个概念之一,
- 一次操作或多次操作中,要么所有的操作全部都得到了执行,并且不受到任何因素的干扰而中断。
- 要么所有的操作都不执行,多个操作步骤是一个不能分割的整体。
- 原子性就是拒绝多线程的,不论是单核还是多核,只要是具备原子性的量。同一时刻只能有一个线程对他进行操作。
- 整个操作过程中,不会被线程调度器中断的操作,都可以认为是具有原子性的。
- 比如:a=1,就是具有原子性的,a++或a+=1或a=a+1 他就不是原子性的操作。
- Java中的原子性操作:
- 1、基本数据类型的读取和赋值操作,并且赋值必须是值给变量,变量之间的相互赋值就不属于原子性操作。
- 2、Java中提供了一个包,整个包里面所有的类都属于原子性操作:java.util.concurrent.atomic
- 可见性:
- 指的是当多个线程访问同一个变量的时候,一个线程改变了这个变量的值,其他线程能够立即看到修改的值。
- 在多线程环境下啊,一个线程对共享变量进行了操作对于其他线程是不可见的,Java提供了volatile来保证共享变量的可见性。
- 当一个变量被volatile修饰之后,表示线程的本地内存无效,当一个线程修改了共享变量之后它会立刻被更新到主内存中,
- 其他的线程读取共享变量的时候,就可以直接到主内存读取。
- synchronized和Lock也可以保证共享变量的可见性。因为synchronized和Lock是保证同一时刻只有一个线程能够获取锁,
- 并且执行同步代码,并且在释放锁之前会对变量的修改进行刷新,刷新到主内存中,所以才能保证可见性。
- volatile:
- 能够保证可见性,但是不能保证原子性。
- 当我们定义了一个volatile修饰的变量后,虚拟机会把这个线程的本地内存中变量强制的刷新到主内存中。
- 这个线程的写操作就会导致其他线程的volatile变量缓存无效。而是直接去主内存找变量。
- volatile不能保证原子性。我们可以加锁,加锁之后,同一时间只能被一个线程执行,这种数据我们成为临界区数据,操作它的代码就称为临界区代码。
- 这样count++就成了原子操作。
- 原子性类:AtomicInteger
- AtomicInteger是从JDK1.5开始提供的java.util.concurrent.atomic里一个类,这个包里面的原子操作类提供了一种方法:
- 可以比较高效的,简单的更新一个变量的方式。
- atomic里面提供了13种类,属于4种类型的原子更新方式,分别是:
- 原子更新基本数据类型
- 原子更新数组
- 原子更新引用
- 原子更新属性
- 原子更新基本数据类型:
- AtomicInteger 原子更新整数类型
- AtomicBoolean 原子更新布尔类型
- AtomicLong 源自更新长整数类型
- 以AtomicInteger类型举例:
- 构造方法:
- 无参构造:new AtomicInteger() 初始化一个默认值(0)的原子类型Integer
- 有参构造:new AtomicInteger(int v) 初始化一个指定参数的原子类型Integer
- 普通方法:
- int get() 获取值
- int incrementAndGet() 以原子方式将当前的值+1,返回的是自增后的值
- int addAndGet(int delta) 以原子方式将参数添加到值。以原子的方式将输入的值和对象中值相加,得到最后的结果。
- int getAndSet(int value) 以原子方式设置为新的value值,并且把旧的值返回
- AtomicInteger的内存分析以及基本原理
- AtomicInteger原理是自旋锁+CAS算法
- CAS算法全称是Compare And Swap 也就是 比较和交换。
- 算法公式 CAS(V,E,N)
- V:表示要更新的变量
- E:表示预期的值
- N:表示新值
- 如果V值等于E值,就把V的值设置为N,如果V值不等于E值,就说名已经有了其他线程完成了更新。
- 那么当前线程就什么都不做。就是CAS需要我们提供一个期望值,当期望值和当前线程的变量值相同,
- 说明还没有线程修改这个值,那么当前线程就可以进行修改,也就是执行CAS操作,如果期望值和
- 当前线程不符,说明这个值已经被其他线程修啊给i了,这个时候就不执行更新操作了,但是可以
- 重新选择读取这个变量,再次尝试修改这个变量,或者干脆放弃操作。
- 有3个操作数,内存的值V,表示要更新的变量。 旧的预期值E。要修改的值N
- 如果旧的预期值E==内存值V,表示修改成功,就是把V改为N
- 如果旧的预期值E!=内存值V,表示修改失败,不做任何操作。
- 并且重新获取现在的最新值,这个重新获取的动作就是【自旋】
- 乐观锁:CAS是从乐观状态的角度出发,它认为每次获取数据,别人都没有修改过,那么它就可以直接修改共享数据的值。
- CAS不会上锁,只会在修改共享数据之前,检查一些别人有没有修改这个数据。
- 悲观锁:synchronized是一个悲观锁的状态,总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改了数据,所以每次操作共享数据之前,都会上锁。
- CAS和synchronized相同点,都可以保证共享数据的安全性。
- 不同点:CAS是乐观锁,synchronized是悲观锁。
|