并发操作之——并发编程三要素

并发操作

并发操作之——并发编程三要素。


并发操作之——并发编程三要素

  • 并发操作
  • 前言
  • 一、原子性
  • 二、有序性:
  • 三、可见性:
  • 总结


前言

并发操作之——并发编程三要素。


一、原子性

一个不可再被分割的颗粒,原子性指的是一个或多个操作要么全部执行成功要么全部执行失败,期间不能被中断,也不存在上下文切换,线程切换会带来原子性的问题

int num = 1; // 原子操作
num++; // 非原子操作,从主内存读取num到线程工作内存,进行 +1,再把num写到主内存, 除非用原子类,即java.util.concurrent.atomic里的原子变量类

解决办法是可以用synchronized 或 Lock(比如ReentrantLock) 来把这个多步操作“变成”原子操作,但是volatile,前面有说到不能修饰有依赖值的情况

public class XdTest {
    private int num = 0;
    
    //使用lock,每个对象都是有锁,只有获得这个锁才可以进行对应的操作
    Lock lock = new ReentrantLock();
    public  void add1(){
        lock.lock();
        try {
            num++;
        }finally {
            lock.unlock();
        }
    }
    
    //使用synchronized,和上述是一个操作,这个是保证方法被锁住而已,上述的是代码块被锁住
    public synchronized void add2(){
        num++;
    }
}

解决核心思想:把一个方法或者代码块看做一个整体,保证是一个不可分割的整体

二、有序性:

程序执行的顺序按照代码的先后顺序执行,因为处理器可能会对指令进行重排序
JVM在编译java代码或者CPU执行JVM字节码时,对现有的指令进行重新排序,主要目的是优化运行效率(不改变程序结果的前提)

int a = 3 //1
int b = 4 //2
int c =5 //3 
int h = a*b*c //4
​
上面的例子 执行顺序1,2,3,42,1,3,4 结果都是一样,指令重排序可以提高执行效率,但是多线程上可能会影响结果
​
假如下面的场景,正常是顺序处理
//线程1
before();//处理初始化工作,处理完成后才可以正式运行下面的run方法
flag = true; //标记资源处理好了,如果资源没处理好,此时程序就可能出现问题
//线程2
while(flag){
    run(); //核心业务代码
}

指令重排序后,导致顺序换了,程序出现问题,且难排查

//线程1
flag = true; //标记资源处理好了,如果资源没处理好,此时程序就可能出现问题
//线程2
while(flag){
run(); //核心业务代码
}
before();//处理初始化工作,处理完成后才可以正式运行下面的run方法

三、可见性:

一个线程A对共享变量的修改,另一个线程B能够立刻看到

// 线程 A 执行
int num = 0;
// 线程 A 执行
num++;
// 线程 B 执行
System.out.print("num的值:" + num);


线程A执行 i++ 后再执行线程 B,线程 B可能有2个结果,可能是0和1。

因为 i++ 在线程A中执行运算,并没有立刻更新到主内存当中,而线程B就去主内存当中读取并打印,此时打印的就是0;也可能线程A执行完成更新到主内存了,线程B的值是1。
所以需要保证线程的可见性
synchronized、lock和volatile能够保证线程可见性

总结

>并发操作之——并发编程三要素。

你可能感兴趣的:(并发,java,并发,并发编程,高并发,高并发编程)