原子性.txt 5.8 KB

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