多线程面试题及答案_第1页
多线程面试题及答案_第2页
多线程面试题及答案_第3页
多线程面试题及答案_第4页
多线程面试题及答案_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

多线程面试题及答案一、选择题(30分)1.在Java中,以下哪个关键字用于确保多个线程对共享资源的正确访问?A.synchronizedB.volatileC.transientD.final答案:A解析:synchronized关键字用于确保多个线程对共享资源的正确访问,它提供了内置的锁机制。volatile关键字用于确保变量的可见性,但不提供原子性。transient关键字用于标记不需要序列化的字段。final关键字用于标记不可变的变量或方法。在多线程环境中,synchronized是最常用的同步机制之一。2.关于Java中的ThreadLocal,以下说法正确的是:A.ThreadLocal是线程安全的B.ThreadLocal可以用于解决多线程环境下的共享变量问题C.每个线程都有自己的ThreadLocal实例副本D.ThreadLocal中的变量可以被所有线程访问答案:C解析:ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本而不会影响其他线程。ThreadLocal本身不是线程安全的,它主要用于解决多线程环境下的线程隔离问题,而非共享变量问题。ThreadLocal中的变量只能被当前线程访问,其他线程无法直接访问。3.以下关于Java内存模型(JMM)的说法,错误的是:A.JMM定义了线程和主内存之间的抽象关系B.JMM规定了volatile变量的所有写操作都会立即刷新到主内存C.JMM保证了所有线程都能看到一致的操作执行结果D.JMM中的happens-before原则定义了内存可见性的保证答案:C解析:Java内存模型(JMM)定义了线程和主内存之间的抽象关系,规定了volatile变量的所有写操作都会立即刷新到主内存,并且happens-before原则定义了内存可见性的保证。但是JMM并不保证所有线程都能看到一致的操作执行结果,这正是并发编程需要解决的核心问题。4.在Java中,以下哪个方法可以用来创建一个新线程?A.implementsRunnableB.extendsThreadC.implementsCallableD.以上都可以答案:D解析:在Java中,可以通过多种方式创建线程:实现Runnable接口并传入Thread构造函数、继承Thread类并重写run方法、实现Callable接口并使用FutureTask包装后创建线程。这三种方式都可以用来创建新线程,但各有特点和适用场景。5.关于线程池,以下说法错误的是:A.线程池可以减少创建和销毁线程的开销B.Java提供了Executor框架来管理线程池C.线程池的大小应该设置为CPU核心数的两倍D.无限创建线程可能导致系统资源耗尽答案:C解析:线程池确实可以减少创建和销毁线程的开销,Java提供了Executor框架来管理线程池,无限创建线程可能导致系统资源耗尽。但是线程池的大小不应该简单地设置为CPU核心数的两倍,而应该根据具体的应用场景、任务类型和系统资源来合理设置。对于计算密集型任务,线程数可以设置为CPU核心数;对于IO密集型任务,线程数可以设置为CPU核心数的数倍。6.在Java中,以下哪个方法可以用来中断一个正在运行的线程?A.stop()B.interrupt()C.destroy()D.suspend()答案:B解析:interrupt()方法可以用来中断一个正在运行的线程,它会设置线程的中断状态。stop()和destroy()方法已经被废弃,因为它们会直接终止线程,可能导致资源无法释放或不一致状态。suspend()方法也已经废弃,因为它可能导致死锁。7.关于Java中的锁,以下说法正确的是:A.ReentrantLock比synchronized更灵活B.synchronized锁是非公平的C.ReentrantLock不能实现可重入性D.synchronized锁可以响应中断答案:A解析:ReentrantLock比synchronized更灵活,它提供了可中断、可轮询、定时锁等高级功能。synchronized锁既可以是公平的也可以是非公平的,默认是非公平的。ReentrantLock也是可重入的,与synchronized类似。synchronized锁不能响应中断,而ReentrantLock可以。8.在Java中,以下哪个集合类是线程安全的?A.ArrayListB.HashMapC.VectorD.LinkedList答案:C解析:Vector是线程安全的集合类,它的方法都是同步的。ArrayList、HashMap和LinkedList都不是线程安全的,它们的方法没有同步机制。如果需要在多线程环境中使用这些集合,可以使用Collections工具类提供的同步包装方法,或者使用并发包中的集合类,如ConcurrentHashMap。9.关于Java中的volatile关键字,以下说法错误的是:A.volatile可以保证变量的可见性B.volatile可以保证原子性C.volatile禁止指令重排序D.volatile适用于状态标志答案:B解析:volatile可以保证变量的可见性,禁止指令重排序,适用于状态标志等场景。但是volatile不能保证原子性,对于复合操作(如i++),volatile无法保证原子性,需要使用synchronized或其他原子类。10.在Java中,以下哪个方法可以用来让当前线程休眠一段时间?A.sleep()B.wait()C.yield()D.join()答案:A解析:sleep()方法可以用来让当前线程休眠指定的时间。wait()方法会使当前线程等待,直到其他线程调用notify()或notifyAll()。yield()方法会让当前线程放弃CPU资源,但线程调度器可能不会选择其他线程。join()方法会等待指定线程执行完成。11.关于Java中的死锁,以下说法正确的是:A.死锁是指多个线程互相等待对方释放资源B.死锁可以通过增加超时机制来避免C.死锁可以通过破坏"请求与保持"条件来避免D.以上说法都正确答案:D解析:死锁确实是指多个线程互相等待对方释放资源,可以通过增加超时机制来避免死锁,也可以通过破坏"请求与保持"条件(即一次性申请所有需要的资源)来避免死锁。这些都是避免死锁的有效方法。12.在Java中,以下哪个接口可以用于获取异步执行的结果?A.RunnableB.CallableC.FutureD.Executor答案:C解析:Future接口可以用于获取异步执行的结果,它提供了检查计算是否完成、等待计算完成、获取计算结果等方法。Runnable接口没有返回值。Callable接口有返回值,但本身不提供获取结果的方法。Executor接口主要用于执行任务。13.关于Java中的原子类,以下说法错误的是:A.AtomicInteger提供了原子操作B.原子类的底层使用CAS算法实现C.原子类可以完全替代synchronizedD.原子类适用于高并发场景答案:C解析:AtomicInteger确实提供了原子操作,原子类的底层使用CAS算法实现,原子类适用于高并发场景。但是原子类不能完全替代synchronized,因为原子类只针对单个变量的原子操作,而synchronized可以保护一段代码的原子性。14.在Java中,以下哪个方法可以用来唤醒一个正在等待的线程?A.notify()B.notifyAll()C.interrupt()D.以上都可以答案:D解析:notify()方法可以用来唤醒一个正在等待的线程(具体唤醒哪个线程由线程调度器决定),notifyAll()方法可以唤醒所有正在等待的线程,interrupt()方法可以中断一个正在运行的线程,如果该线程正在等待,也会抛出InterruptedException异常,从而"唤醒"该线程。15.关于Java中的线程状态,以下说法错误的是:A.NEW状态表示线程已被创建但尚未启动B.RUNNABLE状态表示线程正在运行或准备运行C.BLOCKED状态表示线程正在等待获取锁D.TERMINATED状态表示线程已经结束运行答案:B解析:NEW状态表示线程已被创建但尚未启动,RUNNABLE状态表示线程可以被调度执行(包括正在运行和准备运行),BLOCKED状态表示线程正在等待获取锁,TERMINATED状态表示线程已经结束运行。在Java中,RUNNABLE状态实际上包含了传统操作系统中的"运行中"和"就绪"两种状态。二、填空题(20分)1.在Java中,创建线程有三种方式:继承Thread类、实现______接口和使用______接口。答案:Runnable,Callable解析:在Java中,创建线程有三种主要方式:继承Thread类并重写run方法、实现Runnable接口并将其实现实例传给Thread构造函数、实现Callable接口并使用FutureTask包装后创建线程。这三种方式各有特点,适用于不同的场景。2.Java内存模型(JMM)定义了______和______之间的抽象关系,以及线程之间如何通过内存进行交互。答案:线程,主内存解析:Java内存模型(JMM)定义了线程和主内存之间的抽象关系,以及线程之间如何通过内存进行交互。JMM规定了所有的变量都存储在主内存中,每个线程都有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行,不能直接读写主内存中的变量。3.在Java中,使用synchronized关键字修饰方法时,锁的是该对象的______。答案:监视器锁解析:在Java中,使用synchronized关键字修饰方法时,锁的是该对象的监视器锁。对于实例方法,锁的是当前对象(this);对于静态方法,锁的是该类的Class对象。同一个时间点,只有一个线程能持有对象的监视器锁。4.线程池的核心参数包括:核心线程数、最大线程数、______、工作队列和______。答案:线程存活时间,线程工厂解析:线程池的核心参数包括:核心线程数(corePoolSize)、最大线程数(maximumPoolSize)、线程存活时间(keepAliveTime)、工作队列(workQueue)和线程工厂(threadFactory)。这些参数共同决定了线程池的行为和性能。5.在Java中,______关键字可以确保变量的可见性,但不能保证原子性;而______可以确保变量的原子性。答案:volatile,Atomic类解析:在Java中,volatile关键字可以确保变量的可见性,但不能保证原子性;而Atomic类(如AtomicInteger、AtomicLong等)可以确保变量的原子性。volatile适用于状态标志等简单场景,而Atomic类适用于计数器等需要原子操作的场景。6.在Java中,线程的六种状态分别是:NEW、RUNNABLE、______、WAITING、TIMED_WAITING和______。答案:BLOCKED,TERMINATED解析:在Java中,线程的六种状态分别是:NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(无限等待)、TIMED_WAITING(定时等待)和TERMINATED(终止)。这些状态描述了线程从创建到消亡的整个生命周期。7.在Java中,使用______方法可以让当前线程放弃CPU资源,但线程调度器可能不会选择其他线程;而使用______方法可以让当前线程等待指定线程执行完成。答案:yield(),join()解析:在Java中,使用yield()方法可以让当前线程放弃CPU资源,但线程调度器可能不会选择其他线程;而使用join()方法可以让当前线程等待指定线程执行完成。yield()是一种谦让机制,join()是一种线程同步机制。8.在Java中,______接口可以用于获取异步执行的结果,它提供了检查计算是否完成、等待计算完成、获取计算结果等方法。答案:Future解析:在Java中,Future接口可以用于获取异步执行的结果,它提供了检查计算是否完成、等待计算完成、获取计算结果等方法。Future接口是Java并发编程中非常重要的一个接口,它使得异步编程变得更加简单和可控。9.在Java中,死锁的四个必要条件是:互斥条件、请求与保持条件、______和______。答案:不可抢占条件,循环等待条件解析:在Java中,死锁的四个必要条件是:互斥条件、请求与保持条件、不可抢占条件和循环等待条件。只要破坏其中任何一个条件,就可以避免死锁的发生。例如,可以通过破坏"请求与保持"条件(即一次性申请所有需要的资源)来避免死锁。10.在Java中,使用______关键字可以确保类的实例化是线程安全的;而使用______关键字可以确保变量的值不会被修改。答案:synchronized,final解析:在Java中,使用synchronized关键字可以确保类的实例化是线程安全的;而使用final关键字可以确保变量的值不会被修改。synchronized通过锁机制保证线程安全,final通过不可变性保证线程安全。三、判断题(10分)1.在Java中,使用synchronized关键字修饰的方法和方法块是线程安全的。答案:正确解析:synchronized关键字通过内置的锁机制确保同一时间只有一个线程可以执行被synchronized修饰的方法或方法块,从而保证了线程安全性。当一个线程进入synchronized方法或方法块时,它会获取对象的监视器锁,其他线程必须等待该线程释放锁后才能进入。2.在Java中,volatile关键字可以保证复合操作的原子性。答案:错误解析:volatile关键字只能保证变量的可见性和禁止指令重排序,但不能保证复合操作的原子性。对于i++这样的复合操作,volatile无法保证原子性,需要使用synchronized或其他原子类。3.在Java中,使用线程池可以减少创建和销毁线程的开销,提高系统性能。答案:正确解析:线程池通过重用已经创建的线程,减少了创建和销毁线程的开销,提高了系统性能。特别是在需要频繁创建和销毁线程的场景下,使用线程池可以显著提高系统的响应速度和资源利用率。4.在Java中,ThreadLocal可以为每个线程提供独立的变量副本,从而解决线程安全问题。答案:正确解析:ThreadLocal确实可以为每个线程提供独立的变量副本,每个线程只能访问自己的副本,从而避免了线程安全问题。ThreadLocal适用于需要线程隔离的场景,如用户会话管理、数据库连接管理等。5.在Java中,使用wait()方法前必须先获取对象的锁。答案:正确解析:在Java中,使用wait()方法前必须先获取对象的锁,否则会抛出IllegalMonitorStateException异常。这是因为wait()方法是Object类的方法,它必须在同步块或同步方法中调用,以确保线程拥有对象的锁。6.在Java中,使用stop()方法可以安全地终止一个正在运行的线程。答案:错误解析:stop()方法已经被废弃,因为它会直接终止线程,可能导致资源无法释放或不一致状态。正确的方法是使用interrupt()方法中断线程,让线程有机会完成清理工作后再退出。7.在Java中,HashMap不是线程安全的,而ConcurrentHashMap是线程安全的。答案:正确解析:HashMap确实不是线程安全的,在多线程环境下使用HashMap可能会导致数据不一致。ConcurrentHashMap是线程安全的,它使用了分段锁等技术来提高并发性能,是Java并发包中常用的线程安全集合类。8.在Java中,使用synchronized关键字修饰静态方法时,锁的是该类的Class对象。答案:正确解析:在Java中,使用synchronized关键字修饰静态方法时,锁的是该类的Class对象,而不是类的实例对象。这意味着对于同一个类的所有实例,只有一个线程可以执行该类的静态同步方法。9.在Java中,使用yield()方法可以保证当前线程让出CPU后,其他线程一定会获得执行机会。答案:错误解析:yield()方法只是建议当前线程让出CPU资源,但线程调度器可能不会选择其他线程,而是继续让当前线程执行。yield()不能保证其他线程一定会获得执行机会,这取决于线程调度器的实现和策略。10.在Java中,死锁一旦发生,只能通过终止JVM来解除。答案:错误解析:死锁一旦发生,不一定需要通过终止JVM来解除。可以通过检测死锁并终止其中一个或多个线程来解除死锁。Java提供了ThreadMXBean等工具来检测和管理死锁。此外,通过合理的设计和编程实践,也可以避免死锁的发生。四、简答题(20分)1.简述Java中线程的创建方式及其优缺点。答案:Java中线程的创建主要有三种方式:(1)继承Thread类:-优点:实现简单,可以直接使用this访问当前线程。-缺点:Java不支持多重继承,继承Thread类后不能再继承其他类。(2)实现Runnable接口:-优点:避免了单继承的限制,多个线程可以共享同一个Runnable实例。-缺点:无法直接获取线程的返回值,需要通过共享变量或其他方式获取结果。(3)实现Callable接口:-优点:可以有返回值,可以抛出异常,适合需要返回结果的异步任务。-缺点:需要使用FutureTask包装后才能创建线程,实现相对复杂。在实际应用中,推荐使用实现Runnable或Callable接口的方式,因为它们更加灵活,避免了单继承的限制,并且符合"组合优于继承"的设计原则。2.解释Java内存模型(JMM)中的happens-before原则及其作用。答案:Java内存模型(JMM)中的happens-before原则定义了内存可见性的保证,它规定了在什么情况下一个线程对变量的写入对另一个线程是可见的。happens-before原则包括以下规则:(1)程序顺序规则:在一个线程中,书写在前面的代码happens-before书写在后面的代码。(2)监视器锁规则:对一个锁的解锁happens-before后续对这个锁的加锁。(3)volatile变量规则:对一个volatile变量的写操作happens-before后续对这个变量的读操作。(4)线程启动规则:线程的start()方法happens于此线程的每一个动作。(5)线程终止规则:线程中的所有操作都happens-before对此线程的终止检测。(6)传递性:如果Ahappens-beforeB,且Bhappens-beforeC,那么Ahappens-beforeC。happens-before原则的作用是确保在并发编程中,程序员可以通过这些规则来预测程序的行为,避免不必要的同步,同时保证内存可见性和有序性。它为Java并发编程提供了清晰的语义模型,使得并发程序的正确性可以被验证。3.简述Java中线程池的工作原理及核心参数。答案:Java中线程池的工作原理:(1)当提交一个新任务时,线程池首先检查当前运行的线程数是否小于核心线程数(corePoolSize),如果小于,则创建新线程来处理任务。(2)如果当前运行的线程数大于或等于核心线程数,则将任务加入工作队列(workQueue)。(3)如果工作队列已满,且当前运行的线程数小于最大线程数(maximumPoolSize),则创建新线程来处理任务。(4)如果工作队列已满且当前运行的线程数大于等于最大线程数,则执行拒绝策略(rejectedExecution)。(5)当线程数超过核心线程数时,空闲线程的存活时间超过keepAliveTime,则会被终止。线程池的核心参数:(1)corePoolSize:核心线程数,线程池中保持的活跃线程数,即使它们处于空闲状态。(2)maximumPoolSize:最大线程数,线程池中允许的最大线程数。(3)keepAliveTime:线程存活时间,当线程数超过核心线程数时,空闲线程在终止前等待新任务的最长时间。(4)workQueue:工作队列,用于存放等待执行的任务。(5)threadFactory:线程工厂,用于创建新线程。(6)handler:拒绝策略,当线程池和队列都满时,用于处理新提交的任务。合理配置这些参数对于线程池的性能至关重要,需要根据具体的应用场景和任务类型来调整。4.什么是死锁?如何预防和避免死锁?答案:死锁是指两个或多个线程因为互相等待对方持有的资源而无限期阻塞的情况。死锁的发生需要满足四个必要条件:(1)互斥条件:资源不能被共享,只能由一个线程使用。(2)请求与保持条件:线程已经至少持有一个资源,但又请求了其他资源,而该资源被其他线程持有。(3)不可抢占条件:资源不能被抢占,只能由持有者自愿释放。(4)循环等待条件:存在线程资源的循环等待链。预防和避免死锁的方法:(1)破坏互斥条件:如果资源可以被共享,就不会发生死锁。但这种方法通常不适用,因为某些资源本质上就是互斥的。(2)破坏请求与保持条件:一次性申请所有需要的资源,避免边申请边保持。(3)破坏不可抢占条件:允许线程抢占其他线程持有的资源。但这种方法通常也不适用,因为它可能导致资源状态不一致。(4)破坏循环等待条件:对所有资源进行排序,线程只能按照固定的顺序申请资源。(5)使用超时机制:为锁设置超时时间,避免无限等待。(6)使用死锁检测工具:定期检查系统中是否存在死锁,并采取相应的措施。(7)避免嵌套锁:尽量避免在一个锁中获取另一个锁的情况。在实际应用中,通常采用资源排序、超时机制和避免嵌套锁等方法来预防和避免死锁。设计良好的并发程序应该尽量避免死锁的发生,而不是在死锁发生后才去处理。五、计算题(10分)1.有一个共享变量count初始值为0,三个线程同时对其进行1000次加1操作。如果不使用任何同步机制,请计算可能的最终结果,并解释原因。答案:可能的最终结果:可能是0到3000之间的任意整数,包括但不限于0、1、2...3000。原因分析:count++操作实际上包含三个步骤:(1)读取count的当前值(2)将当前值加1(3)将新值写回count在多线程环境下,如果没有同步机制,这些步骤可能会交错执行,导致数据不一致。例如:-假设count初始值为0-线程A读取count的值为0-线程B读取count的值为0-线程A将0加1得到1,写回count-线程B将0加1得到1,写回count在这种情况下,虽然两个线程都执行了一次加1操作,但count的最终值只增加了1,而不是预期的2。由于三个线程同时执行1000次加1操作,最终结果取决于这些操作的交错情况。最理想的情况下,所有操作都不交错,最终结果为3000;最坏的情况下,所有操作都完全交错,最终结果可能为0。因此,可能的最终结果是0到3000之间的任意整数。易错警示:许多人可能会认为即使没有同步机制,最终结果至少也是3000,这是错误的。由于操作的原子性问题,最终结果可能远小于预期的值。2.有一个生产者-消费者模型,生产者每生产一个产品就通知消费者,消费者消费一个产品后通知生产者。假设缓冲区大小为1,初始时缓冲区为空。请使用wait()和notify()方法实现生产者-消费者模型,并计算当生产者和消费者各执行3次后,缓冲区的状态。答案:使用wait()和notify()方法实现的生产者-消费者模型:```javapublicclassProducerConsumer{privateintbuffer=0;//缓冲区,初始为空privatefinalObjectlock=newObject();//锁对象//生产者方法publicvoidproduce()throwsInterruptedException{synchronized(lock){while(buffer==1){//如果缓冲区已满,等待lock.wait();}buffer=1;//生产产品System.out.println("生产者生产了一个产品,缓冲区状态:"+buffer);lock.notify();//通知消费者}}//消费者方法publicvoidconsume()throwsInterruptedException{synchronized(lock){while(buffer==0){//如果缓冲区为空,等待lock.wait();}buffer=0;//消费产品System.out.println("消费者消费了一个产品,缓冲区状态:"+buffer);lock.notify();//通知生产者}}}```当生产者和消费者各执行3次后,缓冲区的状态分析:(1)生产者第一次执行:缓冲区从0变为1,状态为1(2)消费者第一次执行:缓冲区从1变为0,状态为0(3)生产者第二次执行:缓冲区从0变为1,状态为1(4)消费者第二次执行:缓冲区从1变为0,状态为0(5)生产者第三次执行:缓冲区从0变为1,状态为1(6)消费费者第三次执行:缓冲区从1变为0,状态为0因此,当生产者和消费者各执行3次后,缓冲区的最终状态为0(空)。注意:这个实现假设生产者和消费者交替执行。如果多个生产者或消费者同时执行,需要更复杂的实现来避免竞争条件。另外,使用while循环而不是if语句来检查条件可以防止虚假唤醒问题。六、材料综合题(10分)1.阅读以下Java代码,分析其可能存在的问题,并提出改进方案。```javapublicclassCounter{privateintcount=0;publicvoidincrement(){count++;}publicintgetCount(){returncount;}publicstaticvoidmain(String[]args){Countercounter=newCounter();Runnabletask=()->{for(inti=0;i<1000;i++){counter.increment();}};Threadthread1=newThread(task);Threadthread2=newThread(task);thread1.start();thread2.start();try{thread1.join();thread2.join();}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("Finalcount:"+counter.getCount());}}```答案:问题分析:(1)线程安全问题:increment()方法中的count++操作不是原子操作,它包含读取、修改和写回三个步骤。在多线程环境下,这些步骤可能会交错执行,导致数据不一致。(2)可能的输出:虽然我们期望的最终结果是2000(两个线程各执行1000次加1操作),但由于线程安全问题,实际输出可能是0到2000之间的任意整数。(3)内存可见性问题:如果没有适当的同步机制,一个线程对count的修改可能不会立即对其他线程可见。改进方案:方案1:使用synchronized关键字```javapublicsynchronizedvoidincrement(){count++;}```方案2:使用ReentrantLock```javaprivatefinalReentrantLocklock=newReentrantLock();publicvoidincrement(){lock.lock();try{count++;}finally{lock.unlock();}}```方案3:使用AtomicInteger```javaprivateAtomicInteger

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论