Java线程进阶-并发编程
多线程优缺点
优点: 可以提高程序执行效率
缺点: 线程多了,需要操作系统进行管理的,占用开销的(不是啥事情都创建线程执行的.)
多个线程同时访问共享资源(数据)会出现问题
并发(高并发): 计算机领域指的是同一时刻只能有一个任务执行,多个任务依次执行
汉语并发指的是同时执行
并发执行: 在很多用户同时访问时,应当让多个用户并发执行(一个时间段内依次执行)
并行执行: 在同一个时间点上,多个任务同时执行
并发编程核心问题
多个线程同时对同一个资源访问的情况下. 为什么会出现问题.
不可见性
首先了解java程序运行的内存模型(JMM)
java在线程操作时,先把主内存中的数据加载到线程的工作内存中,然后对数据进行操作,
但是线程A在自己的工作内存中操作玩出,线程B不知道,做了同样的操作,最终结果和预期结果不一样
乱序性(无序性)重排序
系统为了优化,在执行某些指令时,会将一些看起来没关系的指令执行顺序改变,但是在,某些情况下会产生问题.
1 int a = 10;
2 int b = 从数据库读取
3 int c = a+b;
非原子性
i++;在多线程情况下时线程安全的吗?
i++高级语言,在底层执行时,会被分为3条件`
加载i
i+1
i=2
线程的切换执行会带来原子性问题
java内存模型为每个线程提供工作内存(缓存),导致不可见性问题
系统指令优化会把一些指令顺序重排序(在执行一些较为耗时的指令时,把一些其他指令先行执行),可能导致程序无法正常执行
线程切换执行会打破指令执行的原子性 ,例如++操作 分为三条指令, 但是在执行这3条指令时,cpu可能在执行时,切换到其他线程执行,打破原子性执行.
如何解决以上3个问题:
volatile
volatile关键可以解决不可见性和乱序性
**volatile**修饰的共享变量,在被一个线程操作后,可以做到立即对其他线程可见 (volatile底层实现 内存屏障)
volatile修饰的变量禁止对其的执行顺序重排序
volatile只能解决不可见性和重排序问题,
不能解决非原子性问题
如何解决非原子性问题:
加锁(ReentrantLock和synchronized)
使用原子类来解决++在多线程中非原子性问题
AtomicInteger 是一个原子类,能够在不加锁的情况下,实现多线程++操作不出问题,结果正确
private static AtomicInteger atomicInteger = new AtomicInteger(0); atomicInteger.incrementAndGet(); 自增并获得
CAS 机制
CAS(Compare-And-Swap) :比较并交换
特点: 不加锁实现对变量++操作保证原子性.
优点: 线程不会进入到阻塞状态,一直自旋,效率高
缺点: 线程一直自旋,对cpu开销大, 所以原子类适用于线程少的情况