Java并发编程原理与实战九:synchronized的原理与使用

一、理论层面

内置锁与互斥锁

修饰普通方法、修饰静态方法、修饰代码块

package com.roocon.thread.t3;

public class Sequence {
    private static int value;

    // synchronized放在普通方法上,内置锁就是当前方法的实例
    public synchronized int getNext(){
        return value++;
    }

    // synchronized修饰静态方法,内置锁就是当前的Class字节码对象Sequence.class
    public static synchronized int getPrevious(){
        return value--;
    }

    public int xx(){
        // synchronized修饰静态代码块,则锁的是任意一个对象
        /*
        synchronized (this){ }
        synchronized (Integer.valueOf(value)) { }
        synchronized (Sequence.class) { }
         */
        synchronized (Sequence.class) {
            if (value > 0) {
                return value;
            }else {
                return -1;
            }
        }
    }
    public static void main(String[] args) {
        Sequence sequence = new Sequence();
        new Thread(new Runnable() {
            @Override
            public void run() {
               while (true){
                   System.out.println(Thread.currentThread().getName()+" "+sequence.getNext());
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.currentThread().getName()+" "+sequence.getNext());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println(Thread.currentThread().getName()+" "+sequence.getNext());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

 

二、JVM层面

查看xx方法同步代码块字节码:

查看同步方法字节码:

任何对象都可以作为锁,那么锁信息又存在对象的什么地方呢?

存在对象头中。

 

对象头中的信息:

  Mark Word

  Class Metadata Address 类的类型地址

  Array Length

 

1.偏向锁:

每次获得锁和释放锁会浪费资源(消耗时间),很多情况下,竞争锁不是由多个线程,而是由一个线程在使用。线程在获取锁时,会依据对象头Mark word信息进行判断执行。

对象头Mark word中会保存如下信息:

  线程id

  Epoch

  对象的分代年龄信息

  是否是偏向锁

  锁标志位

偏向锁在获取锁之后,如果没有竞争,也就是一直是这个线程在获取锁,那么当这个线程第二次再来进入该方法时,不需要再去获取锁了,也不需要释放锁,这样,就节省了大量的获取锁释放锁的资源。那么,什么时候会释放锁呢?只有当存在竞争时,才会去释放锁。

偏向锁适用于什么场景?

只有一个线程在访问同步代码块的场景。

 

2.轻量级锁:可以同时让多个线程进入同步代码块。自旋锁就是轻量级锁。

轻量级锁是如何加锁的?

在线程执行同步代码块之前,jvm会先在当前线程的栈帧中创建用于存储锁记录的存储空间。

(栈帧是什么?虚拟机栈中存储的是一个一个的栈帧,栈帧中存储了方法的执行信息,每个方法都会伴随着栈帧的进栈和出栈)

然后呢,并将对象头中的mark word复制到锁记录中。然后呢,开始竞争锁就可以了。竞争成功之后,markword就改变了,会将锁标志位改成轻量级锁。接着,开始执行同步体。

另外一个线程也想获得该锁。同样,它也将对象头中的mark word复制到锁记录中,它发现已经被其他线程获得了锁,所以它修改不成功。于是,它就不停的去修改,不停的失败,直到第一个线程把这个锁释放了,它就可以修改成功了。刚才这一个过程,就是所谓的自旋锁。

 

3.重量级锁

什么是重量级锁?就是,这个线程获得锁进入之后,其他线程必须在外面等待。synchronized就是重量级锁。

 

三、总结

上面只是对synchronized 如何实现同步功能的一个视频学习总结,更加细节的深入原理可以参考下面两篇文章:

synchronized的实现原理    

深入理解Java并发之synchronized实现原理

 

参考资料:

《java并发编程与实战》龙果学院

posted on 2018-07-25 23:53  pony1223  阅读(715)  评论(0编辑  收藏  举报

导航