并发编程的3个重要概念
1.原子性:
一个操作或者多个操作,要么全部成功,要么全部失败
1.java中保证了基本数据类型的读取和赋值,保证了原子性,这些操作不可终端
a=10 原子性 b=a 不满足 1.read a; 2.assign b; c++ 不满足 1.read c; 2 add 3.assige to c c=c+1 不满足 1.read c 2.add; 3. assign to c;
2.可见性:
volatile 可以保证可见性.主要是把变量放在主存里
多个线程访问这个变量,一个线程修改之后必须保证另一个线程可以看见。
每一个线程都自己的缓存,有的变量在主存区,我们要保证变量的可见性
4.顺序性:
java中hapens-before 原则保证了有序性
4.1 代码顺序(程序顺序规则): 单个线程中的每个操作 编写在前面的反生在编写在后面的
4.2 lock原则(监视器锁规则) : unlock 必须发生在lock之后
4.3 volatile变量规则: 对该变量的写操作必须发生在读操作之前.就是一个变量 写变量发生在读变量之前
4.4 传递规则: a->b->c 那么a肯定在c之前
4.5 start规则: 先start 线程在启动(个人认为废话)
4.6 中断规则: 对线程的中断 先发生在 捕获interpetor异常之前(个人认为废话)
4.7 对象的销毁规则: 一个对象的初始化.必须发生在finalize 之前(个人认为废话)
4.8 线程的终结规则: 所有操作都发生在线程死亡之前 (个人认为废话)
比如定义一个变量
int i =0; boolean flag =false; int i =1; boolean flag =true; java中有重排序的过程 因为这几条代码没有关联性可能会变成 int i =0; boolean flag =false; boolean flag =true; int i =1;
重排序只保证最终一致性,重排序也是有依赖关系的,重排序对没有依赖关系的可以重排序 ,重排序不会影响单线程,但是会影响多线程
举个例子
--------thread-1 ==========
obj =createObj();1
flag = true; 2
如果这里发生指令重排将 1 -2 顺序颠倒下面下面的代码就会报出空指针异常
-----thread-2------
while(!flag){
}
useObject()
atomic 具有原子性
主要有俩点 1.他的变量是volatile
2.unsafe方法调用
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value;
什么时候需要使用atomic类对象
当多线程操作一个变量的时候。如果使用传统的变量会出错,很多同学为了解决这个问题,加了votatile修饰。也并不能够解决问题。
补充下votalite 的知识
volatile具有可见性、有序性,不具备原子性。
当value+=1时候我们需要做几个操作
1.从主存区获取变量到写存区
2.value+1
3.value = 新的value
4.讲变量写入主存区
这4步都是原子性的。但是在多线程环境中。可能你读取到的变量是别人修改过的变量造成线程不安全情况
private static Set<Integer> set = Collections.synchronizedSet(new HashSet<>()); public static void main(String[] args) throws InterruptedException { Thread t1= new Thread(()->{ int x=0; while (x<500){ set.add(value); int temp =value; System.out.println(Thread.currentThread().getName()+" "+temp); value +=1; x++; } }); Thread t2= new Thread(()->{ int x=0; while (x<500){ set.add(value); int temp =value; System.out.println(Thread.currentThread().getName()+" "+temp); value +=1; x++; } }); Thread t3= new Thread(()->{ int x=0; while (x<500){ set.add(value); int temp =value; System.out.println(Thread.currentThread().getName()+" "+temp); value +=1; x++; } }); t1.start(); t2.start(); t3.start(); t1.join(); t2.join(); t3.join(); System.out.println(set.size()); 」