V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mawerss1
V2EX  ›  Java

为了理解 jvm 对 synchronized 的优化写了个 Java 层的偏向锁 轻量级锁

  •  
  •   mawerss1 · 38 天前 · 1045 次点击
    这是一个创建于 38 天前的主题,其中的信息可能已经有所发展或是发生改变。
    package code.ss.demo1.jvm;
    
    import java.util.ArrayList;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class BiasLock {
    
        public static final int SPIN_THRESHOLD_TIMES = 300;
    
        static long a = 0;
        static class LockThread extends Thread{
            AtomicReference<LockObject> lock = new AtomicReference<>();
    
            public LockThread(Runnable target) {
                super(target);
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            LockObject lockObject = new LockObject();
            ArrayList<String> strings = new ArrayList<>();
            int size = 30;
            Thread[] threads = new Thread[size];
    
            for (int i = 0; i < size; i++) {
                threads[i] =
                new LockThread(() -> {
                    int b = 0;
                    while (b <= 10) {
                        lock(lockObject);
                        a++;
                        strings.add(String.valueOf(a));
                        unlock(lockObject);
                        b++;
                    }
    
                });
            }
            for (Thread thread : threads) {
                thread.start();
            }
    
            for (Thread thread : threads) {
                thread.join();
            }
            System.out.println("size:" + strings.size());
            System.out.println(a);
            assert a == (size * 10);
        }
    
    
        static class LockObject {
            //0 is not biased,1 is biased
            AtomicInteger lockStatus = new AtomicInteger(0);
    
            AtomicInteger baisedThreadId = new AtomicInteger(-1);
            ReentrantLock reentrantLock = new ReentrantLock();
            AtomicReference<Thread> smallLockReference = new AtomicReference();
            AtomicInteger spainThreadCount = new AtomicInteger(0);
            AtomicInteger spinCount = new AtomicInteger(0);
        }
        public static final int BlOCK = 5;
        public static final int SPIN_LOCK = 2;
        public static final int NO_LOCK = 0;
        public static final int LOCK_BAISED = 1;
    
        static public void unlock(LockObject lock) {
            int status = lock.lockStatus.get();
            if (status == NO_LOCK) {
                return;
            }
            if (status == LOCK_BAISED) {
                //解除偏向
                int i = lock.baisedThreadId.get();
                if (lock.baisedThreadId.compareAndSet(i, -1)) {
                    lock.lockStatus.compareAndSet(LOCK_BAISED, NO_LOCK);
                }
            }
            if (status == SPIN_LOCK) {
                Thread thread = Thread.currentThread();
                lock.smallLockReference.compareAndSet(thread, null);
            }
            if (status == BlOCK) {
                if (lock.smallLockReference.get() == Thread.currentThread()) {
                    //spin lock
                    lock.smallLockReference.set(null);
                }else{
                    lock.reentrantLock.unlock();
                }
            }
        }
    
        static public void lock(LockObject lock) {
            int status = lock.lockStatus.get();
            System.out.println("status:" + status);
            if (status == BlOCK) {
                lock.reentrantLock.lock();
            } else if (status == SPIN_LOCK) {
                    raiseSpinLock(lock);
    //            if (lock.smallLockReference.get() != null) {
    //                if (lock.smallLockReference.get()  == Thread.currentThread()) {
    //                    return;
    //                }
    //            }else{
    //                lock.lockStatus.compareAndSet(SPIN_LOCK, BlOCK);
    //                lock.reentrantLock.lock();
    //            }
            } else if (status == NO_LOCK) {
                if (lock.baisedThreadId.get() == -1) {
                    boolean b = lock.baisedThreadId.compareAndSet(0, Thread.currentThread().hashCode());
                    if (b) {
                        if (lock.lockStatus.compareAndSet(NO_LOCK, LOCK_BAISED)) {
                            return;
                        }
                    }
                }
                raiseSpinLock(lock);
            } else if (status == LOCK_BAISED) {
                //已偏向
                if (lock.baisedThreadId.get() == Thread.currentThread().hashCode()) {
                    return;
                }
                lock.lockStatus.set(SPIN_LOCK);
                //升级到轻量级锁
                raiseSpinLock(lock);
    
            }
    
        }
    
        private static void notify_lock(LockObject lock) {
    //        LockSupport.unpark();
        }
    
        private static void block_lock(LockObject lock) {
    //        LockSupport.park();
        }
    
        private static void raiseSpinLock(LockObject lock) {
            while (true) {
                int status = lock.lockStatus.get();
                if (status == BlOCK) {
                    lock.reentrantLock.lock();
                    return;
                }
    
                if (status <= SPIN_LOCK) {
                    if (lock.lockStatus.compareAndSet(status, SPIN_LOCK)) {
                        break;
                    }
                }
            }
    
    
            LockThread c = (LockThread) Thread.currentThread();
            c.lock.compareAndSet(null, lock);
            int spinCount = 0;
            while (true) {
                if (spinCount >= SPIN_THRESHOLD_TIMES) {
                    lock.lockStatus.set(BlOCK);
                    lock.reentrantLock.lock();
                    break;
                }
                int i = lock.spainThreadCount.incrementAndGet();
                if (i > 3) {
                    System.out.println("// stop spin cause too many thread contend,go to block lock");
                    lock.lockStatus.set(BlOCK);
                    lock.reentrantLock.lock();
                    break;
                }
                if (lock.smallLockReference.compareAndSet(null, c)) {
                    //get lock success
                    lock.spainThreadCount.decrementAndGet();
                    System.out.println("//spin :" + spinCount);
                    break;
                } else {
                    Thread.yield();
                    spinCount++;
                }
                lock.spinCount.incrementAndGet();
            }
        }
    
    
    }
    
    

    交流下,理解有问题吗?

    6 条回复    2021-10-21 21:30:00 +08:00
    AoEiuV020
        1
    AoEiuV020   38 天前
    用代码交流理论上是可以的,但应该没多少人会看见一段代码就花时间理解其中思想,
    我是说建议楼主同时用中文简述一下自己的想法,
    wooyulin
        2
    wooyulin   38 天前
    同楼上,思路先说说吧,不然不知道值不值得看
    cubecube
        3
    cubecube   38 天前   ❤️ 1
    偏向锁在 jdk17 都被删除了。没有看的必要了。
    BQsummer
        4
    BQsummer   38 天前
    我看了这帖子才知道偏向锁要被删除了,查了下,原因是偏向锁提高不了多少性能,代码复杂度提高了。
    https://openjdk.java.net/jeps/374
    lei2j
        5
    lei2j   38 天前
    LZ 能讲一下设计思想吗,不然不太好理解(我比较菜鸡)
    mawerss1
        6
    mawerss1   38 天前 via iPhone   ❤️ 1
    @cubecube 对,更好的理解为什么被删
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   912 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 20:54 · PVG 04:54 · LAX 12:54 · JFK 15:54
    ♥ Do have faith in what you're doing.