java材料整理(重点)

全栈知识点

关键字: synchronized详解 | Java 全栈知识体系


1.java反射机制

1.1 类加载过程

1.1.1 类的加载过程

源文件经过编译后得到.class文件,被jvm加到内存中,在运行时加载和执行;

加载过程:加载——连接(验证——准备——解析)——初始化

1.1.2 双亲委派模型

加载时自下往上获取类是否加载,加载时从上往下尝试加载,保证类只被加载一次,运行期间一个类只有一个class对象产生;

Bootstrap ClassLoader启动类加载器

Extension ClassLoader扩展类加载器

App ClassLoader应用程序类加载器

继承ClassLoader自定义类加载器

静态加载和动态加载:

new()初始化一个类视为静态加载,加载时异常是error,NoClassDefFoundError;

class.forName()视为动态加载(运行时可改变程序结构和变量类型),加载时异常ClassNotFoundException,checked异常,写代码时需要catch

1.2.java反射机制

反射机制运行时可获取任何一个类的方法和属性,可调用任何对象的方法和属性

反射获取属性和方法:

1.object——getClass,需要已经有类的对象存在;

2.静态的class属性,如String.class,可返回相关联的class对象,不需要类的对象存在;

3.class类的静态方法forName(String className)  --className为全路径包名

通过反射生成对象:

1.class对象的newInstance()

2.通过class对象——Constructor对象——Constructor的newInstance()创建对象 -- 可指定构造器类的实例,newInstance()方法的本质是调用类的无参Public构造方法

获取某个类的构造方法 getDeclaredConstructor(不分public);

获取类的成员方法: getMethod(),getDeclaredMethod(不分public)

获取类的成员变量(成员属性:getDeclaredField(不分public),获取私有setAccessible(true)

java基础之反射机制_悟笙的博客-CSDN博客_java 反射

Java基础篇:反射机制详解_张维鹏的博客-CSDN博客_java反射机制原理详解

Java虚拟机:对象创建过程与类加载机制、双亲委派模型_张维鹏的博客-CSDN博客


3.AQS(AbstractQuenedSynchronizer 抽象的队列式同步器)

3.1.核心思想:

被请求的共享资源空闲——当前请求资源的线程设置为有效,共享资源设置为锁定状态;

如果被请求的共享资源被占用——线程阻塞等待,被唤醒时锁分配的机制,用CLH队列(虚拟双向队列,无队列实例,通过节点关联)锁实现;

volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒;

3.2.资源共享方式

独占式:exclusive, 只有ReentrantLock可以执行;

共享:share,有CountDownLatch、ReadWriteLock等可以执行;

同时实现独占和共享两种方式:如ReentrantReadWriteLock;

AQS详解(面试)_mulinsen77的博客-CSDN博客_aqs

可重入锁,共享模式,互斥锁(源码解析)

Java并发之AQS详解 - waterystone - 博客园

LockSupport:

阻塞park(),释放unpark(),无先后顺序,permit只有0和1,多次park()不会累加;

syncronized:

等待wait(), 唤醒notify(),成对出现,有先后顺序,通过底层monitor对象完成,

    不需要手动释放锁,

    非公平锁,不可中断,

    唤醒机制:随机唤醒或者全部唤醒(notifyAll());

作用域

不能被继承

加在对象时,同步的对象是实例,同步的是非静态方法

加在类XXXX.class或者代码块(方法)(可以对静态方法同步

synchronized 作用域问题_程序媛-CSDN博客_synchronized作用域

深入分析Synchronized原理(阿里面试题) - aspirant - 博客园

synchronized的四种作用域以及不能被继承解析 - 从此寂静无声 - 博客园

ReentrantLock

Sync类实现需要手动释放锁

    可设置,true 公平锁(FIFO),false非公平锁

    中断,中断方法:

    tryLock(long timeout TimeUnit unit),

    lockInterruptibly()放代码块中调用,调用interrupt方法可中断

   唤醒机制: 可设置条件精准唤醒,前驱节点的出队或阻塞线程被中断

深入理解 AQS 底层实现原 park unpar理_庸人庸的博客-CSDN博客_aqs

ReentrantLock公平锁和非公平锁加锁和释放过程(源码解析和流程图讲解比较清晰)

对比:

相同:可协调多线程同步;互斥锁和可见;可重入锁

不同:

1. ReentrantLock显示地获得,释放锁,synchronized隐式获得释放锁

2. ReentrantLock可响应中断,可轮回,synchronized是不可以响应中断的

3. ReentrantLock是API级别的,synchronized是JVM级别的

4. ReentrantLock可以实现公平锁

5. ReentrantLock通过Condition可以绑定多个条件

6. 底层实现不一样,synchronized是同步阻塞,使用的是悲观并发策略,lock是同步非阻塞,采用的是乐观并发策略。

7. Lock是一个接口,而synchronized是java中的关键字,synchronized是内置的语言实现

8. synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很可能造成死锁现象, 因此使用 Lock 时需要在 finally 块中释放锁。

synchronized和ReentrantLock的区别_淮水竹亭-CSDN博客

java面试题:java中断,synchronized和ReentrantLock能否中断_我是方小磊的博客-CSDN博客_reentrantlock中断

3.3.CAS底层原理

比较交换技术,内存地址V,期望值E,新值N,经过对比期望值与 V,决定是否交换,相等将N更新

a-B-a问题,添加version版本号控制

多次比较效率低,某个线程取值与期望值一直不相等可能无限循环

1)确保对内存的读-改-写操作原子执行。处理器会使用总线锁 或 缓存锁来保持原子性。

2)禁止该指令,与之前和之后的读和写指令重排序。

3)把CPU写缓冲区中的所有数据刷新到内存中,使其它CPU缓存失效。

深入图解AQS实现原理和源码分析_Seky_fei的博客-CSDN博客

CAS原理解析 CAS底层 - YoungDeng - 博客园


5.java多线程并发

Java中如何保证线程安全性_阿梨喜欢吃榴莲的博客-CSDN博客_如何保证线程安全

如何确保线程安全_笑笑生的博客-CSDN博客_怎么确保线程安全

5.1 final原理

修饰的是一个基本数据类型数据:这个数据的值在初始化后将不能被改变;;

当final修饰的是一个引用类型数据时:也就是修饰一个对象时, 引用在初始化后将永远指向一个内存地址, 不可修改. 但是该内存地址中保存的对象信息可以修改

修饰的类时,该类不可被继承,该类的方法隐式指定为final方法;

修饰方法时,该方法不能被重写

深入理解final关键字(详解)_念念不忘,必有回响-CSDN博客_final关键字

浅析Java中的final关键字 - Matrix海子 - 博客园

5.2 volatile原理

共享和防止指令重排序,但是不能保证原子性

lock指令:

    锁总线,其它CPU对内存的读写请求都会被阻塞,直到锁释放,不过实际后来的处理器都采用锁缓存替代锁总线;

    lock后的写操作会回写已修改的数据,同时让其它CPU相关缓存行失效,从而重新从主存中加载最新的数据

可见性:写操作StoreStore屏障,写操作之后StoreLoad,将最新变量刷回内存;读之前load读取主内存的最新变量,读之后LoadLoad和LoadStore内存屏障

深入理解Volatile关键字及其实现原理_泽宇的博客-CSDN博客

Volatile禁止指令重排序(三) - MXC肖某某 - 博客园

volatile是怎么保障内存可见性以及防止指令重排序的?_做最亮的星星-CSDN博客_volatile如何保证内存可见性

atomicLong:长整形进行原子操作

incrementAndGet()

volatile修饰value ,CAS更改

volatile和Atomic_dzyls的笔记-CSDN博客

Java原子类--AtomicLong - ken007 - 博客园

5.3 线程池

线程的生命周期:创建start——就绪runnable(阻塞)——run——stop

方便线程的管理,可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,减少创建和销毁的内存开销

ThreadPoolExecutor

核心线程数量corePoolSize

最大线程数量maximumPoolSize

keepAliveTime:核心线程以外的线程回收时间设置

workQueue:FIFIO原则(先进先出)

    SynchronousQueue 直接提交:该QUEUE中,每个插入操作必须等待另一个线程的对应移除操作

    LinkedBlockingQueue 无界队列:所有corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize

    ArrayBlockingQueue 有界队列 不推荐

handler:拒绝策略

    RejectedExecutionHandler:拒绝四种策略

   1. CallerRunsPolicy:线程调用运行该任务的 execute本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度;

    2.AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException

    3.DiscardPolicy:删除,但是不抛出异常

    4.DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的(等待时间最久)任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)

线程池的原理及实现_康志的博客-CSDN博客_线程池的原理

Java线程池的底层实现与使用 - StoneGeek - 博客园

深入理解线程和线程池(图文详解)_weixin_40271838的博客-CSDN博客_线程池

线程池的工作原理与源码解读 - 清泉^_^ - 博客园

5.4 线程的销毁

核心线程超时删除:allowCoreThreadTimeOut=true

1当没有超过核心线程时,不会销毁线程

2当超过核心线程数:再判断,如果超过最大值,则销毁;如果timeout过,则销

java 线程池 配置_Java手动配置线程池过程详解_马斯克·贾的博客-CSDN博客

Java线程池的allowCoreThreadTimeOut参数_weixin_33859231的博客-CSDN博客


6.redis

6.1 分布式锁

保证同一时间只能有一个客户端对共享资源进行操作,解决多个服务资源共享的问题

标志位(唯一标识)+超时设置+finally释放锁

【剑指Offer】Redis 分布式锁的实现原理看这篇就够了_龙台的技术笔记-CSDN博客_redis分布式锁实现原理

Redis实现分布式锁原理与实现分析_jp413670706的专栏-CSDN博客_redis分布式锁实现原理

基础信息

主要依赖物理内存,一个字符串类型最大容量512M

数据类型:String、List、Set、Sorted Set、hashes

史上最全Redis面试题及答案_xiaozhegaa的博客-CSDN博客_redis面试题及答案

zset:

redis zset底层实现原理 - YF-海纳百川 - 博客园

Redis有序集合zset的底层实现 - 简书

6.2 持久化:

RDB机制:

save操作同步或者设置定时自动触发

在指定的时间间隔内生成数据集的时间点快照

(1)RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复

(2)生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。

(3)RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑,快照时父线程修改的数据可能无法保存

AOF机制:

修改时触发;异步,每秒触发;从不触发

 持久化记录服务器执行的所有写操作命令

(1)可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据;

(2)AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损

(3)日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写

(4)AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复

Redis的两种持久化RDB和AOF(超详细)_鲨鱼辣椒灬的博客-CSDN博客_rdb和aof

https://baijiahao.baidu.com/s?id=1654694618189745916&wfr=spider&for=pc

6.3 缓存更新策略

内存溢出淘汰策略:

过期策略:过期;定时

Redis 缓存更新策略_爱与不爱,一念之间-CSDN博客_redis 更新策略

6.4 redis集群

主从模式:master提供读写服务,写操作后同步内存到slave,slave只提供读服务,master挂掉停止写服务

哨兵模式:master挂掉后,会从其他slave选择新的master,每个sentinel以每秒钟一次的频率向它所知的master,slave以及其他sentinel实例发送一个 PING 命令

cluster模式:

Redis集群详解_变成习惯-CSDN博客_redis集群


7.hashMap

7.1 基本原理:

数组和链表的结合,解决修改和查找效率问题,对象entry(包含key,value),1.8后采用Node数组;

    存储时:object.hasncode()获取hashcode(一般的Hash函数为:要存入的数 mod(求余) Hash数组长度),当hashcode相同时,存储在bucket链表的下一个节点;

    取模:是(table.length - 1) & hash,算法直接舍弃了二进制hash值在table.length以上的位,因为那些位都代表table.length的2的n次方倍数。取模的结果就是Node将要放入table的下标。

    获取值时:调用get(key)方法,通过键对象的hashcode找到bucket位置,再调用keys.equals()方法;

    put()方法:第一次initTable()初始化table,长度是 16,计算hash找到位置,通过cas方法加入节点;如果table不为空,hash=i,table[i]为node,遍历链表查找是否有相同的hash值,如有相同hash值替换新的节点,如果没有直接插入新节点;table[i]为treenode,红黑树链表

    扩容:懒加载机制(使用才加载),默认大小为16,负载因子大小为0.75,到达大小时进行rehashing过程,用散列扩容2倍,链表长度>8&&数组的长度>=64——转变为红黑树,根据hash值分为两个子链表;

equals结果为true的hashcode一定一样,为false的hashcode可能一样

7.2 如何减少hash碰撞:

使用String和Integer这wrapper类,尤其String类final不可变,且重写了equalshashcode()方法;

拉链法:目前hashmap使用的方法

rehash:哈希函数,二次哈希函数,,,

开放地址法:(h(key)+di) mod m(链表长度),di取值 1 2 3 ... m-1,线性探测再散列;di=1,每次冲突后后移一位,di=1 -1 2 -2 4 -4 9 -9  ...k*k  -k*k(k<m/2),二次探测再散列;di取值可能为伪随机数列,称伪随机探测再散列

公共溢出区:记载冲突的点

负载因子调整

解决Hash碰撞冲突方法总结_lppl010_的专栏-CSDN博客_hash碰撞解决方法

7.3 线程安全问题

hashtable对比:

    hasnhmap:元素无序,key可以为null, 线程不安全(Map m = Collections.synchronizeMap(hashMap);),效率高,

    hashtable:元素有序,key不可为null, 线程安全(内部有synchronized),效率低 

ConcurrentHashMap

1.初始化table:size标志位,yield(),cas放置Node

2.put()空节点null:cas,volatile

3.put()节点:cas,对当前节点sychronized

4.扩容:hash占位Node-1=MOVED,扩容时可参与帮助扩容

 key不可为null,table数组元素作为锁,对每一行数据进行加锁,并发控制使用Synchronized和CAS来操作;

HashMap底层实现原理及面试问题_疯一样的女子-CSDN博客_hashmap底层实现原理

ConcurrentHashMap是如何实现线程安全的_|-| [- |_ |_ ()-CSDN博客_concurrenthashmap如何保证线程安全

HashMap的底层实现原理 - auldlangsynezh - 博客园

1.8的hashMap源码解析和红黑树

    volatile V val; // get操作全程不需要加锁是因为Node的成员val是用volatile修饰

    volatile Node<K,V> next;    //Node<k,v>[] table,实现了Entry接口,表示链表中的下一个节点,数组用volatile修饰主要是保证在数组扩容的时候保证可见性;

    setValue用final修饰

红黑树查找O(logn)

红黑树通过treemap(有序)实现

Java中HashMap底层实现原理(JDK1.8)源码分析_tuke_tuke的博客-CSDN博客_hashmap底层实现原理

逐行解读HashMap源码之红黑树篇_节点

7.4 JDK1.8的改动点

1、JDK1.7版本锁的粒度是基于Segment的,包含多个HashEntry,而JDK1.8实现降低锁的粒度就是HashEntry(首节点)

2、JDK1.8版本的数据结构变得更加简单,去掉了Segment这种数据结构,使用synchronized来进行同步锁粒度降低,所以不需要分段锁的概念,实现的复杂度也增加了

3、JDK1.8使用红黑树来优化链表,基于长度很长的链表的遍历是一个很漫长的过程,而红黑树的遍历效率是很快的,代替一定阈值的链表,这样形成一个最佳拍档

4、JDK1.8为什么使用内置锁synchronized来代替重入锁ReentrantLock:

- 低粒度加锁方式,synchronized并不比ReentrantLock差,

粗粒度加锁中ReentrantLock可能通过Condition来控制各个低粒度的边界,更加的灵活,而在低粒度中,Condition的优势就没有了

- 在大量的数据操作下,对于JVM的内存压力,基于API的ReentrantLock会开销更多的内存

某个节点的hash值是MOVED,则表示正在进行数组扩张的数据复制阶段

ConcurrentHashMap实现原理及源码分析_快乐小石头的博客-CSDN博客_concurrenthashmap实现原理

7.5 扩容机制


Hashmap实现原理及扩容机制详解_lkforce-CSDN博客_hashmap扩容


8.设计模式

8.1 单例模式

1.单例类只有一个实例对象

2.该单例对象必须由单例类自行创建;

3.单例类对外提供一个访问该单例的全局访问点。

懒汉式和饿汉式,包括线程安全和不安全的实现

8.1.1 懒汉式线程安全实现:

1.初始化变量用volatile修饰Singleton instance;(其中volatile作用是禁止指令重排序

2.双重检验锁模式(double checked locking pattern):是一种使用同步块加锁的方法,称为双重检查锁,因为会有两次检查 instance == null,一次是在同步块外,一次是在同步块内;

8.1.2饿汉式线程安全实现方式:

1.初始化变量用final修饰

2.内部静态类

3.枚举

如何正确地写出单例模式 | Jark's Blog

8.2.设计模式基础

设计原则:可复用,可扩展,可维护

开闭原则:对扩开放,对修改关闭;实现:抽象约束,封装变化

里式替换原则:继承必须确保超类所拥有的性质在子类中仍然成立

依赖倒置原则:要面向接口编程,不要面向实现编程。

Java设计模式:23种设计模式全面解析(超级详细)

8.3 工厂模式

静态工厂模式(简单工厂模式):通过向工厂传递类型来指定要创建的对象

一般工厂模式:抽象工厂,具体工厂,抽象产品,具体产品,将生产任务交给不同的派生类工厂。这样不用通过指定类型来创建对象了

抽象工厂模式:通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干

由于可能封装了大量对象和工厂创建,新加产品需要修改已定义好的工厂相关的类,因此对于产品和工厂的扩展不太友好

设计模式之工厂模式(factory pattern) - alpha_panda - 博客园

8.4 动态代理

代理模式:为其他类提供一个代理来控制对某个对象的访问,代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理

程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法

2.创建被代理的类以及接口

3.通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理

4.通过代理调用方法

细说JDK动态代理的实现原理_痴人说梦-CSDN博客_jdk动态代理

Java JDK 动态代理(AOP)使用及实现原理分析_衣舞晨风-CSDN博客_jdk动态代理


10.ThreadLocal

ThreadLocal是JDK包提供的,它提供线程本地变量,在实际多线程操作的时候,操作的是自己本地内存中的变量——规避了线程安全问题;

两个本地变量inheritableThreadLocals和threadLocals,都是ThreadLocalMap类型的变量,使用完需要 remove(),否则可能一直存在弱引用,会导致内存溢出。

threadLocals不支持继承性,同一个ThreadLocal变量在父线程中被设置值后,在子线程中是获取不到

inheritableThreadLocals子线程中可获取,重写了了childValue、getMap、createMap三个方法

弱引用:当一个线程调用ThreadLocal的set方法设置变量的时候,当前线程的ThreadLocalMap就会存放一个记录,这个记录的key值为ThreadLocal的弱引用

key是弱引用,所以当前线程的ThreadLocalMap里面的ThreadLocal变量的弱引用在gc的时候就被回收,但是对应的value还是存在的这就可能造成内存泄漏(因为这个时候ThreadLocalMap会存在key为null但是value不为null的entry项)

remove方法判断该当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量

Java中的ThreadLocal详解 - CoderSheeper - 博客园


11.JVM

JVM理解 - 随笔分类 - 夏末秋涼 - 博客园

java中的栈、堆、方法区、本地方法区的讲解 - 快乐的派大星 - 博客园

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

推荐阅读更多精彩内容