synchronized原理

原理

synchronized代码块需要指定锁对象监视器,如果是实力方法则默认为实例对象。静态方法则为实例的class对象(全局唯一)。执行synchronized代码进入时会执行monitorenter指令申请对象monitor监视器,如果已经线程获取了则判断是否当前线程,如果是则继续执行,锁计数器递增1,如果不是则阻塞等待。退出synchronized代码块时,锁计数器递减1,如果monitor监视器计数为0,则执行monitorexit指令退出对象monitor监视器。

案例

非静态同步方法

package com.mytest;

/**
 * @author 会灰翔的灰机
 * @date 2021/3/7
 */
public class SyncTest {

    private static int count;

    public synchronized void countAddNormal(int delta) {
        count += delta;
        System.out.println(this + " : " + count);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sleep end");
    }

    public static void main(String[] args) {
        SyncTest syncA = new SyncTest();
        SyncTest syncB = new SyncTest();
        // 线程0,线程1获取syncA对象监视器成功。其余线程等待对象监视器退出释放后继续执行
        for (int i = 0; i < 10; i++) {
            final int j = i;
            new Thread(() -> {
                if (j % 2 == 0) {
                    syncA.countAddNormal(1);
                }
                if (j % 2 == 1) {
                    syncB.countAddNormal(1);
                }
            }).start();
        }
    }
}

输出结果,共两个监视器对象syncA,syncB,所以同时进入的线程只有两个线程持有者(可重入)

com.mytest.SyncTest@2b074fd9 : 1
com.mytest.SyncTest@244c3e9e : 2
sleep end
sleep end
com.mytest.SyncTest@2b074fd9 : 3
com.mytest.SyncTest@244c3e9e : 4
sleep end
sleep end
com.mytest.SyncTest@2b074fd9 : 5
com.mytest.SyncTest@244c3e9e : 6
sleep end
sleep end
com.mytest.SyncTest@2b074fd9 : 7
com.mytest.SyncTest@244c3e9e : 8
sleep end
com.mytest.SyncTest@2b074fd9 : 9
sleep end
com.mytest.SyncTest@244c3e9e : 10
sleep end
sleep end

静态同步方法

package com.mytest;

/**
 * @author 会灰翔的灰机
 * @date 2021/3/7
 */
public class SyncTest {

    private static int count;

    public synchronized void countAddNormal(int delta) {
        count += delta;
        System.out.println(this + " : " + count);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sleep end");
    }

    public synchronized static void countAddStatic(int delta) {
        count += delta;
        System.out.println("static : " + count);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sleep end");
    }

    public static void main(String[] args) {
        // 线程0,获取SyncTest.class对象监视器成功。其余线程等待对象监视器退出释放后继续执行
        for (int i = 0; i < 10; i++) {
            new Thread(() -> SyncTest.countAddStatic(1)).start();
        }
    }
}

输出结果,同时只有一个线程可以获取SyncTest.class对象监视器成功,所以就跟单线程执行结果是没有区别的

static : 1
sleep end
static : 2
sleep end
static : 3
sleep end
static : 4
sleep end
static : 5
sleep end
static : 6
sleep end
static : 7
sleep end
static : 8
sleep end
static : 9
sleep end
static : 10
sleep end

总结

synchronized与ReentrantLock对比,ReentrantLock提供了一些更加灵活的API,ReentrantLock主要有3个优势。jdk1.6及以后版本性能已经不再是它的优势了,synchronized性能与ReentrantLock基本持平

  1. 等待可中断。synchronized是无限期等待,无法定制中断条件,例如:超时时间,线程打断
  2. 可实现公平锁。synchronized是非公平锁竞争,默认ReentrantLock也是非公平锁,但是可以通过boolean类型构造器指定为公平锁
  3. 锁可以绑定多个条件。synchronized中锁对象的wait,notify,notifyAll方法可以实现一个隐含的条件,如果要实现多余一个条件的应用场景则不得不多加一个锁。例如:顺序输出ABC那篇博客的案例(https://blog.csdn.net/u010597819/article/details/113812365?spm=1001.2014.3001.5501

刚才也提到了jdk对synchronized的优化使其性能可以与ReentrantLock持平,优化主要有几个点简单提下:自旋锁和自适应自旋锁,锁消除,锁粗化(锁合并),偏向锁,轻量锁,重量锁(锁膨胀)。

上一篇:Cygwin .a 转为 .lib .dll


下一篇:apche重定向&端口转发&隐藏index.php.htaccess