ReentrantLock 类源码阅读

前面两篇文章介绍
Thread 类源码阅读
AQS 源码阅读
现在开始 可重入锁 ReentrantLock 的代码阅读。
还是一样的套路,一行一行的代码死磕。

/**
 * A reentrant mutual exclusion {@link Lock} with the same basic
 * behavior and semantics as the implicit monitor lock accessed using
 * {@code synchronized} methods and statements, but with extended
 * capabilities.
 * 可重入的互斥{@link Lock},
 * 具有与使用 synchronized 方法和语句访问的隐式监视器锁相同的基本行为和语义,
 * 但具有扩展的功能。
 *
 * <p>A {@code ReentrantLock} is <em>owned</em> by the thread last
 * successfully locking, but not yet unlocking it. A thread invoking
 * {@code lock} will return, successfully acquiring the lock, when
 * the lock is not owned by another thread. The method will return
 * immediately if the current thread already owns the lock. This can
 * be checked using methods {@link #isHeldByCurrentThread}, and {@link
 * #getHoldCount}.
 * ReentrantLock 由上一次成功锁定但尚未解锁的线程所有。
 * 当该锁不属于另一个线程时,调用 lock 的线程将返回,并成功获取该锁。
 * 如果当前线程已经拥有该锁,则该方法将立即返回。
 * 可以使用方法 isHeldByCurrentThread 和 getHoldCount 进行检查。
 *
 * <p>The constructor for this class accepts an optional
 * <em>fairness</em> parameter.  When set {@code true}, under
 * contention, locks favor granting access to the longest-waiting
 * thread.  Otherwise this lock does not guarantee any particular
 * access order.  Programs using fair locks accessed by many threads
 * may display lower overall throughput (i.e., are slower; often much
 * slower) than those using the default setting, but have smaller
 * variances in times to obtain locks and guarantee lack of
 * starvation. Note however, that fairness of locks does not guarantee
 * fairness of thread scheduling. Thus, one of many threads using a
 * fair lock may obtain it multiple times in succession while other
 * active threads are not progressing and not currently holding the
 * lock.
 * 此类的构造函数接受可选的 fairness 参数。
 * 当 fairness 设置为 true  时,在竞争下,锁倾向于授予对等待时间最长的线程的访问。
 * 否则,此锁不能保证任何特定的访问顺序。
 * 使用许多线程访问的公平锁的程序可能会比使用默认设置的程序显示较低的总体吞吐量
 *(即较慢;通常要慢得多),但获得锁的时间变化较小,并保证没有饥饿。
 * 但是请注意,锁的公平性不能保证线程调度的公平性。
 * 因此,使用公平锁的许多线程之一可能会连续多次获得它,
 * 而其他活动线程未进行且当前未持有该锁。
 * Also note that the untimed {@link #tryLock()} method does not
 * honor the fairness setting. It will succeed if the lock
 * is available even if other threads are waiting.
 * 还要注意,未定时的{@link tryLock()}方法不支持公平性设置。
 * 如果锁定可用,即使其他线程正在等待,它将成功。
 *
 * <p>It is recommended practice to <em>always</em> immediately
 * follow a call to {@code lock} with a {@code try} block, most
 * typically in a before/after construction such as:
 *
 *  <pre> {@code
 * class X {
 *   private final ReentrantLock lock = new ReentrantLock();
 *   // ...
 *
 *   public void m() {
 *     lock.lock();  // block until condition holds
 *     try {
 *       // ... method body
 *     } finally {
 *       lock.unlock()
 *     }
 *   }
 * }}</pre>
 *
 * <p>In addition to implementing the {@link Lock} interface, this
 * class defines a number of {@code public} and {@code protected}
 * methods for inspecting the state of the lock.  Some of these
 * methods are only useful for instrumentation and monitoring.
 *
 * <p>Serialization of this class behaves in the same way as built-in
 * locks: a deserialized lock is in the unlocked state, regardless of
 * its state when serialized.
 *
 * <p>This lock supports a maximum of 2147483647 recursive locks by
 * the same thread. Attempts to exceed this limit result in
 * {@link Error} throws from locking methods.
 *
 * @since 1.5
 * @author Doug Lea
 */
public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    private final Sync sync;
    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     * 默认时非公平锁
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     * fair = true: 公平锁,反之,非公平锁
     * 
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
}

上文代码可见:

  • ReentrantLock 实现 Lock 接口;
  • ReentrantLock 持有 Sync 对象
    = Sync 类是 FairSync 和 NonfairSync 的父类
    借助 idea 看下类图:


    image.png

    先看 Lock 接口

/**
 * {@code Lock} implementations provide more extensive locking
 * operations than can be obtained using {@code synchronized} methods
 * and statements.  They allow more flexible structuring, may have
 * quite different properties, and may support multiple associated
 * {@link Condition} objects.
 * Lock 实现提供的锁定操作比使用 synchronized 方法和语句可获得的锁定操作更广泛。
 * 它们允许更灵活的结构,可能具有完全不同的属性,
 * 并且可能支持多个关联的 Condition 对象。
 *
 * <p>A lock is a tool for controlling access to a shared resource by
 * multiple threads. Commonly, a lock provides exclusive access to a
 * shared resource: only one thread at a time can acquire the lock and
 * all access to the shared resource requires that the lock be
 * acquired first. However, some locks may allow concurrent access to
 * a shared resource, such as the read lock of a {@link ReadWriteLock}.
 * 锁是一种用于控制多个线程对共享资源的访问的工具。
 * 通常,锁提供对共享资源的独占访问:
 * 一次只能有一个线程可以获取该锁,对共享资源的所有访问都需要首先获取该锁。
 * 但是,某些锁可能允许并发访问共享资源,例如{@link ReadWriteLock}的读取锁。
 *
 * <p>The use of {@code synchronized} methods or statements provides
 * access to the implicit monitor lock associated with every object, but
 * forces all lock acquisition and release to occur in a block-structured way:
 * when multiple locks are acquired they must be released in the opposite
 * order, and all locks must be released in the same lexical scope in which
 * they were acquired.
 * 使用 synchronized 方法或语句可访问与每个对象关联的隐式监视器锁,
 * 但是强制所有锁的获取和释放以块结构的方式发生:
 * 当获取多个锁时,必须在锁中释放它们。
 * 顺序相反,所有锁必须在获得它们的相同词汇范围内释放
 *
 * <p>While the scoping mechanism for {@code synchronized} methods
 * and statements makes it much easier to program with monitor locks,
 * and helps avoid many common programming errors involving locks,
 * there are occasions where you need to work with locks in a more
 * flexible way. For example, some algorithms for traversing
 * concurrently accessed data structures require the use of
 * &quot;hand-over-hand&quot; or &quot;chain locking&quot;: you
 * acquire the lock of node A, then node B, then release A and acquire
 * C, then release B and acquire D and so on.  Implementations of the
 * {@code Lock} interface enable the use of such techniques by
 * allowing a lock to be acquired and released in different scopes,
 * and allowing multiple locks to be acquired and released in any
 * order.
 * 尽管 synchronized 方法和语句的作用域机制使使用监视器锁进行编程变得更加容易,
 * 并且有助于避免许多常见的涉及锁的编程错误,但是在某些情况下,您需要以更灵活的方式使用锁。
* 例如,某些用于遍历并发访问的数据结构的算法需要使用“移交”或“链锁”:
 * 您获取节点A的锁,然后获取节点B的锁,然后释放A并获取C,然后释放B并获得D等。 
 * Lock 接口的实现通过允许在不同范围内获取和释放锁,
 * 并允许以任意顺序获取和释放多个锁,从而启用了此类技术。
 *
 * @see ReentrantLock
 * @see Condition
 * @see ReadWriteLock
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface Lock {

    /**
     * Acquires the lock.
     *  获取锁
     *
     * <p>If the lock is not available then the current thread becomes
     * disabled for thread scheduling purposes and lies dormant until the
     * lock has been acquired.
     * 如果该锁不可用,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,
     * 直到获得该锁为止。
     *
     * <p><b>Implementation Considerations</b>
     *
     * <p>A {@code Lock} implementation may be able to detect erroneous use
     * of the lock, such as an invocation that would cause deadlock, and
     * may throw an (unchecked) exception in such circumstances.  The
     * circumstances and the exception type must be documented by that
     * {@code Lock} implementation.
     * Lock 实现可能能够检测到锁的错误使用,例如可能导致死锁的调用,
     * 并且在这种情况下可能引发(未经检查的)异常。
     * 环境和异常类型必须由该 Lock 实现来记录。
     */
    void lock();

    /**
     * Acquires the lock unless the current thread is
     * {@linkplain Thread#interrupt interrupted}.
     * 除非当前线程为 Thread#interrupt interrupted,否则获取锁。
     * 
     * <p>Acquires the lock if it is available and returns immediately.
     *  获取锁(如果有)并立即返回。
     * <p>If the lock is not available then the current thread becomes
     * disabled for thread scheduling purposes and lies dormant until
     * one of two things happens:
     * <ul>
     * <li>The lock is acquired by the current thread; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
     * current thread, and interruption of lock acquisition is supported.
     * </ul>
     * 如果该锁不可用,则出于线程调度目的,当前线程将被禁用,
     * 并在发生以下两种情况之一之前处于休眠状态:
     * 该锁是由当前线程获取的;
     * 一些其他线程中断当前线程有,并且支持中断获取锁。
     * <p>If the current thread:
     * <ul>
     * <li>has its interrupted status set on entry to this method; or
     * <li>is {@linkplain Thread#interrupt interrupted} while acquiring the
     * lock, and interruption of lock acquisition is supported,
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared.
     * 如果当前线程:在进入此方法时已设置其中断状态;
     * 或在获取锁时被中断了,并且支持了锁获取的中断,
     * 则抛出 InterruptedException 并清除了当前线程的中断状态。
     *
     * @throws InterruptedException if the current thread is
     *         interrupted while acquiring the lock (and interruption
     *         of lock acquisition is supported)
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * Acquires the lock only if it is free at the time of invocation.
     * 仅在调用时锁可用,才获取该锁。
     *
     * <p>Acquires the lock if it is available and returns immediately
     * with the value {@code true}.
     * 如果锁可用,获取锁并立即返回 true
     * If the lock is not available then this method will return
     * immediately with the value {@code false}.
     * 如果锁不可用,获取锁失败,并立即返回false
     *
     * @return {@code true} if the lock was acquired and
     *         {@code false} otherwise
     */
    boolean tryLock();

    /**
     * Acquires the lock if it is free within the given waiting time and the
     * current thread has not been {@linkplain Thread#interrupt interrupted}.
     * 如果锁在给定的等待时间内是空闲的,并且当前线程尚未被中断,则获取该锁。
     * <p>If the lock is available this method returns immediately
     * with the value {@code true}.
     * If the lock is not available then
     * the current thread becomes disabled for thread scheduling
     * purposes and lies dormant until one of three things happens:
     * <ul>
     * <li>The lock is acquired by the current thread; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
     * current thread, and interruption of lock acquisition is supported; or
     * <li>The specified waiting time elapses
     * </ul>
     * 如果锁可用,则此方法立即返回 true。
     * 如果该锁不可用,则出于线程调度目的,
     * 当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
     * 当前线程获取了该锁;
     * 某些其他线程中断了当前线程,并且支持中断获取锁;
     * 经过指定的等待时间
     *
     * <p>If the lock is acquired then the value {@code true} is returned.
     *  如果线程获取到锁,返回 true
     * <p>If the current thread:
     * <ul>
     * <li>has its interrupted status set on entry to this method; or
     * <li>is {@linkplain Thread#interrupt interrupted} while acquiring
     * the lock, and interruption of lock acquisition is supported,
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared.
     *  如果当前线程:在进入此方法时已设置其中断状态;
     * 或在获取锁时被中断了,并且支持了锁获取的中断,
     * 则抛出 InterruptedException 并清除了当前线程的中断状态。
     * 
     *
     * <p>If the specified waiting time elapses then the value {@code false}
     * is returned.
     * 如果经过了指定的等待时间,则返回 false 。
     * If the time is
     * less than or equal to zero, the method will not wait at all.
     * 如果时间小于或等于零,则该方法将根本不等待。
     *
     * @param time the maximum time to wait for the lock
     * @param unit the time unit of the {@code time} argument
     * @return {@code true} if the lock was acquired and {@code false}
     *         if the waiting time elapsed before the lock was acquired
     *
     * @throws InterruptedException if the current thread is interrupted
     *         while acquiring the lock (and interruption of lock
     *         acquisition is supported)
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * Releases the lock.
     * 释放锁
     */
    void unlock();

    /**
     * Returns a new {@link Condition} instance that is bound to this
     * {@code Lock} instance.
     * 返回一个新的绑定到当前锁的 Condition 对象。
     *
     * <p>Before waiting on the condition the lock must be held by the
     * current thread.
     * A call to {@link Condition#await()} will atomically release the lock
     * before waiting and re-acquire the lock before the wait returns.
     *  在等待条件之前,锁必须由当前线程持有。
     * 对 Condition#await()的调用将在等待之前自动释放该锁,并在等待返回之前重新获取该锁。
     *
     * @return A new {@link Condition} instance for this {@code Lock} instance
     * @throws UnsupportedOperationException if this {@code Lock}
     *         implementation does not support conditions
     */
    Condition newCondition();
}

再看 Sync 类,是 ReentrantLock 的抽象静态内部类。

/**
 * Base of synchronization control for this lock. Subclassed
 * into fair and nonfair versions below. Uses AQS state to
 * represent the number of holds on the lock.
 * ReentrantLock 的同步控制基础。在下面细分为公平和非公平版本。使用AQS状态表示锁的保留数。
 */
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    abstract void lock();

    /**
     * 执行非公平的tryLock
     */
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        //  资源状态可用, cas 占用state 并设置独占锁的拥有者为当前线程,并返回true
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        
        // 如果资源状态 state 不可用,判断独占锁的拥有者是否为当前线程,如果是资源状态 state 加1, 返回true
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        // 获取锁失败,返回false
        return false;
    }

    // 可中断释放独占锁
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        // 如果当前线程 不是 独占锁的拥有者,抛出异常
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        // 如果getState() - releases == 0, 即资源状态可用,将独占锁拥有者置为null
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        // 更新资源状态
        setState(c);
        return free;
    }

    // 当前线程是否拥有独占锁
    protected final boolean isHeldExclusively() {
        // While we must in general read state before owner,
        // we don't need to do so to check if current thread is owner
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    // 创建新的ConditionObject对象
    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    // Methods relayed from outer class
    
    // 获取独占锁的拥有者
    // 如果资源状态为0,返回null
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    // 拥有锁的次数(重入次数)
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    // 判断是否被锁,即资源状态是否可用
    final boolean isLocked() {
        return getState() != 0;
    }

    /**
     * Reconstitutes the instance from a stream (that is, deserializes it).
     * 流反序列化对象
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

接下来是FairSync 和 NonfairSync。

/**
 * Sync object for non-fair locks
 * 非公平锁的同步对象
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     * 执行lock,
     */
    final void lock() {
        // cas 变更资源状态,如果变更成功,设置独占锁拥有者为当前线程
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // 获取独占锁,忽略中断
            acquire(1);
    }
    
    // 独占模式获取锁,非公平
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

/**
 * Sync object for fair locks
 * 公平锁的同步对象
 */
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);
    }

    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     * 获取独占锁,
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        // 资源状态可用,如果没有前置节点,cas 变更资源状态,如果变更成功,设置独占锁拥有者为当前线程,返回 true
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 如果当前线程就是独占锁拥有者,资源状态增加, 返回true
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

继续阅读 ReentrantLock 代码,此处不在做注释,这几个方法都是交由 成员对象 sync 实现具体逻辑,详见。

public void lock() {
    sync.lock();
}

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

public Condition newCondition() {
    return sync.newCondition();
}

public int getHoldCount() {
    return sync.getHoldCount();
}

public boolean isHeldByCurrentThread() {
    return sync.isHeldExclusively();
}

public boolean isLocked() {
    return sync.isLocked();
}

protected Thread getOwner() {
    return sync.getOwner();
}

public final boolean hasQueuedThreads() {
    return sync.hasQueuedThreads();
}

public final boolean hasQueuedThread(Thread thread) {
    return sync.isQueued(thread);
}

public final int getQueueLength() {
    return sync.getQueueLength();
}
    
protected Collection<Thread> getQueuedThreads() {
    return sync.getQueuedThreads();
}   

public boolean hasWaiters(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}   

public int getWaitQueueLength(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}   

protected Collection<Thread> getWaitingThreads(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}   
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容