CAS、Synchronized

高并发——CAS、Synchronized、Volatile

一、CAS(无锁优化、自旋、乐观锁)

 

Compare and Set/Swap 比较并交换

  •     CAS(V,Expected,NewValue)

1. CAS在JAVA最终底层的实现:cmpxchg=cas修改变量值。

2. CAS操作在底层上有一条对应的汇编指令(硬件级别):lock cmpxchg。

3. 多个CPU的话前面要加lock。

4. Cmpxchg指令是非原子性,加上Lock 就保证了原子性 。

5. Synchronized和volatile的底层都有lock指令。

  •  ABA问题  ----加version

二、Synchronized的实现

1. 对象在内存中的存储布局:

 

java -XX:+PrintCommandLineFlags -version

 

 

2.工具查看对象在内存中大小

JOL=Java Object Layout

导入依赖

<dependencies>
        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>
    </dependencies>

 

 

 Markword8

Classpointer本来是8开启指针压缩变4

Instance date0

Padding4   补齐至被8整除

Object o =new Object()在内存中占16字节

 

下图可以看出,锁信息是在markword中

 

 3.锁升级过程

锁升级过程:

new ——>>偏向锁——>>轻量级锁(无锁,自旋锁,自适应自锁) ——>>重量级锁

synchronized优化的过程和markword息息相关。

 

newnew出来对象,没有给它上锁。

偏向锁:;来了一个线程上去直接“贴个标签(线程ID)”,后面可能还是这个线程。(效率高,无竞争)。

轻量级锁(自旋锁):来线程竞争了,撤销偏向锁,升级轻量级锁,线程都有自己的线程栈,在自己线程栈里生成各自的对象:lockrecord

抢的过程是CAS操作,看谁能将自己的lockrecord“贴上去”。用CAS操作讲markword设置为指向自己这个想成的LR的指针。

 

重量级锁:竞争加剧,有线程超过10次自旋,或者自选线程数超过CPU核数的一半,JVM自己控制。升级为重量级锁(向操作系统申请资源)

每一个重量级锁下面有一个队列,锁升级成功相当于进入了锁的队列,在队列里如果没轮到线程执行就不会消耗CPU,线程在wait或阻塞状态。

每个线程都进入队列中“冻死”,什么时候轮到对应的线程,就将其“解冻”。

 

锁消除 lock eliminate

 

 我们都知道StringBuffer是线程安全的,因为它的关键方法都是被synchronized修饰,但我们看上面

这段代码,我们会发现,sb这个引用只会在add方法中使用,不可能被其他线程引用(因为是局部变

量,栈私有),因此sb是不可能共享的资源,JVM会自动消除StringBuffer对象内部的锁。

 

锁粗化 lock coarsening

 

 JVM会检测到这样一连串的操作,都对同一个对象加锁(while 循环内100次执行append,没有锁

粗化的就会进行100次加锁/解锁),此时JVM就会将加锁的范围粗化到这一连串的操作的外部(比如

while虚幻体外),使得这一连串操作只需要加一次锁即可。

 

 

 

 实验(需要安装插件):

 

 

 

 JIT:Just In Time Compiler 即时编译

将热点代码做即时编译,直接编译成机器语言。效率可以高很多

 

 总结:

Synchronized实现过程

1. java代码:synchronized

2.monitorenter moniterexit

3.执行过程中自动升级

4.lock comxchg

 

扩充知识:

一、解决同样更搞笑的方法,使用AtomXXX类

AtoXXX类本身方法都是原子性的,但不能保证多个方法连续调用时原子性的

代码示例:

public class T02_AtomicInteger {
    /*volatile*/ //int count=0;
    AtomicInteger count= new AtomicInteger(0);

    /*synchronized*/void m(){
        for (int i = 0; i <10000 ; i++) {
            count.incrementAndGet();//count++
        }

    }

    public static void main(String[] args) {
        T02_AtomicInteger t=new T02_AtomicInteger();

        List<Thread> threads=new ArrayList<>();
        for (int i = 0; i <10 ; i++) {
            threads.add(new Thread(t::m,"thread-"+i));
        }
        threads.forEach((o)->o.start());
        threads.forEach((o)->{
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(t.count);
    }

点进去AtomicInteger  可以看到Unsafe类(此类java11以后已关闭):

Unsafe=C C++的指针

  • 直接操作内存      - allcateMemory putXX freeMemory pageSize
  • 直接生产类实例   - allocateInstance
  • 直接操作类或实例变量 - objectFieldOffset   -getInt -getObject
  • CAS相关操作:weakCompareAndSetObject  Int  Long

 二、synchronized优化

同步代码块中的语句越少越好,

示例代码 比较m1 m2:

public class FineCoarseLock {
    int count=0;
    synchronized void m1() {
        //do sth neew not sync
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //业务逻辑中只有下面这句需要sync,这时不应该给整个方法加sync
        count++;
        //do sth neew not sync
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
        void m2(){
            //do sth neew not sync
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //业务逻辑中只有下面这句需要sync,这时不应该给整个方法加sync
            //采用细粒度的锁,可以使线程争用时间变短,从而提高效率。
            synchronized (this){
                count++;
            }
            //do sth neew not sync
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }

三、synchronized可以保证可见性和原子性,volatile只能保证可见性

public class T03 {
    /*volatile*/ int count=0;

    synchronized void m(){
        for (int i=0;i<10000;i++){
            count++;
        }
    }

    public static void main(String[] args) {
        T03 t=new T03();
        List<Thread> threads=new ArrayList<>();
        for (int i = 0; i <10 ; i++) {
            threads.add(new Thread(t::m,"thread-"+i));
        }
        threads.forEach((o)->o.start());
        threads.forEach((o)->{
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(t.count);
    }

 

posted @ 2021-01-18 11:49  派大靖  阅读(191)  评论(0)    收藏  举报