关于AbstractOwnableSynchronizer类解惑

今天刚看了一点Java并发编程实践,随手拿着Java源码分析了一下。

 

看到ReadWriteLock部分兴起,跟着源代码看到了ReentrantReadWriteLock,对其实现的读写分离锁原理非常好奇,平时都是基于如何使用,但是对其实现机制不甚了解,于是驱动自己看了看源码

 

大体上明白了其中的原理,就是设置了两个标识:

 

第一个exclusiveOwnerThread,标识线程是否是Write线程,如果有线程设置,则认为该线程为写线程,其它线程必须等待。

 

第二个标识是:state,该标识是读写线程会修改该值。

 

有点疑惑的是:

 

 

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    /** Use serial ID even though all fields transient. */
    private static final long serialVersionUID = 3737899427754241961L;

    /**
     * Empty constructor for use by subclasses.
     */
    protected AbstractOwnableSynchronizer() { }

    /**
     * The current owner of exclusive mode synchronization.
     */
    private transient Thread exclusiveOwnerThread;

    /**
     * Sets the thread that currently owns exclusive access. A
     * <tt>null</tt> argument indicates that no thread owns access.
     * This method does not otherwise impose any synchronization or
     * <tt>volatile</tt> field accesses.
     */
    protected final void setExclusiveOwnerThread(Thread t) {
        exclusiveOwnerThread = t;
    }

    /**
     * Returns the thread last set by
     * <tt>setExclusiveOwnerThread</tt>, or <tt>null</tt> if never
     * set.  This method does not otherwise impose any synchronization
     * or <tt>volatile</tt> field accesses.
     * @return the owner thread
     */
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

 

 这个类中关于exclusiveOwnerThread的读写居然没有加锁保护,并且代码注释上也明确说明不需要这个锁保护。

 

 

这让我困惑不已,为啥不需要加锁保护,从代码的调用结构上看try*系列的方法都会对该值作变更,这不就已经涉及到多线程操作了吗?

 

后来想了想,自己给了个说法:

 

看了看try*系列方法实现,可以列举其中一个方法说明:

 

final boolean tryWriteLock() {
            Thread current = Thread.currentThread();
            int c = getState();
            if (c != 0) {
                int w = exclusiveCount(c);
                if (w == 0 ||current != getExclusiveOwnerThread())
                    return false;
                if (w == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
            }
            if (!compareAndSetState(c, c + 1))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }
 

 

 该方法中w == 0 ||current != getExclusiveOwnerThread() 这句代码十分关键,这就是这句代码主导了不使用同步锁也能保证判断的正确性。

 

首先分析有两种线程有可能会调用该方法:第一种写线程,第二种读线程

 

关键点:一个线程对一个变量的前后读写,在本线程是始终可以看到结果的。

 

如果是写线程调用的时候,那么肯定是该写线程首先对exclusiveOwnerThread进行了赋值写入(获取锁定),那么在同一个线程中赋值是可见的,也就是在接下来的释放过程中,并不需要内存同步。

 

如果是读线程调用的时候,唯一有可能发生意外的情况是:

该读线程是由前面的写线程转变而来,可能会想到current == getExclusiveOwnerThread()成立的情况,但是这种情况是不存在的,因为要从写线程转变成读线程,首先就是得有释放写锁的过程(如果没有锁的释放过程,那么程序逻辑本身就存在错误了),也就是有了setExclusiveOwnerThread(null)的过程,那么转变过后的读线程应该看到的null或者是锁被释放过后其它线程设置的值,而非以前本线程在身为写线程时数值。

 

注意区别:读线程和写线程中获取读锁的区别,这里的读线程是指只获取读锁的线程,与写线程中先获取写锁,然后再获取读锁是有区别的。

 

如果该线程是从上一次调用的写线程转化而来,那么这次看到的自己的内存exclusiveOwnerThread应该是null,一定不会是自己。那就保证 if (w == 0 ||current != getExclusiveOwnerThread())正确性了。

 

一句话就是:如果是写线程那么看到的exclusiveOwnerThread一定是自己,如果是读线程那么看到的exclusiveOwnerThread一定不是自己!

 

那么有可能读到不是最新的exclusiveOwnerThread值已经不重要!重要的是保证了如果是读线程,那么current != getExclusiveOwnerThread()一定正确!

你可能感兴趣的:(abstract)