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

2021-10-21 17:15:14 +08:00
 mawerss1
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();
        }
    }


}

交流下,理解有问题吗?

1976 次点击
所在节点    Java
6 条回复
AoEiuV020
2021-10-21 17:19:30 +08:00
用代码交流理论上是可以的,但应该没多少人会看见一段代码就花时间理解其中思想,
我是说建议楼主同时用中文简述一下自己的想法,
wooyulin
2021-10-21 18:28:32 +08:00
同楼上,思路先说说吧,不然不知道值不值得看
cubecube
2021-10-21 19:20:43 +08:00
偏向锁在 jdk17 都被删除了。没有看的必要了。
BQsummer
2021-10-21 19:46:50 +08:00
我看了这帖子才知道偏向锁要被删除了,查了下,原因是偏向锁提高不了多少性能,代码复杂度提高了。
https://openjdk.java.net/jeps/374
lei2j
2021-10-21 20:04:40 +08:00
LZ 能讲一下设计思想吗,不然不太好理解(我比较菜鸡)
mawerss1
2021-10-21 21:30:00 +08:00
@cubecube 对,更好的理解为什么被删

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/809581

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX