thread1.如何保证线程安全通过合理的时间调度,避免共享资源的访问冲突。此外,在并行任务的设计中可以采用适当的策略,保证任务之间不共享资源,可以设计一种规则,保证一个客户端的计算工作和数据访问只由一个线程或一个线程完成worker机,而不是将一个客户端的计算工作分配给多个线程来完成。2.线程的基本状态及状态之间的关系。running表示运行状态。Runnable表示就绪状态(万事俱备,只欠CPU)。Blocked表示阻塞状态。阻塞状态有很多种情况,可能是调用wait()方法进入等待池,也可能是执行同步方法或同步代码块进入等待锁池,或者调用sleep()方法或join()方法等待睡眠或其他线程结束,或者因为I/O中断。3、线程池(threadpool)在面向对象编程中,创建和销毁对象是非常耗时的,因为创建一个对象需要获取内存资源或者其他更多的资源。在Java中更是如此,虚拟机将尝试跟踪每个对象,以便在对象被销毁后对其进行垃圾回收。因此,提高服务程序效率的一种方法是尽可能减少对象的创建和销毁次数,尤其是一些资源密集型对象的创建和销毁次数。这就是“池化资源”技术的由来。顾名思义,线程池就是预先创建若干个可执行线程,并将它们放入一个池(容器)中。需要时,可以从池中获取线程,无需自己创建。使用后,线程不需要销毁,而是放回池中,从而减少了创建和处理的需要。销毁线程对象的开销。Java5+中的Executor接口定义了执行线程的工具。它的子类型,线程池接口,是ExecutorService。线程池的配置比较复杂,尤其是线程池的原理不是很清楚的时候,所以在工具类Executors上提供了一些静态工厂方法来生成一些常用的线程池,如下:newSingleThreadExecutor:创建一个单线程线程池。这个线程池只有一个线程在工作,相当于用一个线程串行执行所有任务。如果唯一的线程异常结束,一个新的线程将取代它。这个线程池保证所有任务的执行顺序按照任务提交的顺序执行。newFixedThreadPool:创建一个固定大小的线程池。每次提交任务时都会创建一个线程,直到线程达到线程池的最大大小。一旦线程池的大小达到最大值,它将保持不变。如果一个线程因为异常执行而结束,线程池会补充一个新的线程。newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需的线程数,一些空闲线程(60秒内没有执行任务)将被回收。当任务数量增加时,线程池可以智能地添加新的线程来处理任务。这个线程池不限制线程池的大小,线程池的大小完全取决于操作系统(或JVM)可以创建的最大线程大小。newScheduledThreadPool:创建一个无限大小的线程池。这个线程池支持定时和周期性的执行任务。newSingleThreadExecutor:创建单线程线程池。这个线程池支持定时和周期性的执行任务。4.同步与异步如果系统中存在临界资源(资源数量小于竞争资源的线程数量),例如正在写入的数据可能稍后被另一个线程读取,或者正在读取的数据可能已经被另一个线程读取过,一旦写入,那么数据必须同步访问(数据库操作中的独占锁就是最好的例子)。当应用程序调用对象上的方法执行时间很长并且不希望程序等待方法返回时,应该使用异步编程。在许多情况下,使用异步方法通常效率更高。其实,所谓同步是指阻塞操作,而异步是指非阻塞操作。5、线程同步和线程调度相关方法wait():将一个线程置于等待(阻塞)状态,并释放持有对象的锁;sleep():让一个正在运行的线程休眠,是一个Static方法,调用这个方法来处理InterruptedException异常;notify():唤醒一个处于等待状态的线程,当然调用这个方法的时候,是不可能唤醒某个处于等待状态的线程的,而是由JVM来决定唤醒哪个线程线程,与优先级无关;notityAll():唤醒所有处于等待状态的线程,该方法不会把对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;通过Lock接口提供显式锁机制(explicitlock),增强了灵活性和线程协调性。Lock接口定义了加锁(lock())和解锁(unlock())的方法,还提供了newCondition()方法生成Condition对象,用于线程间的通信;此外,Java5还提供了信号量,可以使用信号量来限制可以访问共享资源的线程数。在访问资源之前,线程必须获得信号量的权限(调用信号量对象的acquire()方法);线程完成对资源的访问后,必须将权限返回给信号量(调用信号量对象的release()方法)。6、一个线程进入一个对象的synchronized方法A,然后其他线程是否可以进入这个对象的synchronized方法B。其他线程只能访问对象的非同步方法,同步方法无法进入。因为非静态方法上的synchronized修饰符要求在方法执行时获取对象的锁,如果已经进入A方法,对象锁已经被拿走,那么试图进入B方法的线程只能等待锁池(注意不是等待池哦)等待对象的锁。要执行同步方法,您需要获取锁。进入方法A意味着对象锁已经被拿走了,方法B不能再执行了。7、线程的sleep()方法和yield()方法有什么区别?考虑线程的优先级,优先级低的线程会得到运行的机会;yield()方法只会给相同优先级或更高优先级的线程一个运行的机会;线程执行完sleep()方法后会进入sleep()方法的阻塞(blocked)状态,执行完yield()方法后,转入就绪(ready)状态;sleep()方法语句抛出InterruptedException,yield()方法没有声明任何异常;sleep()方法比yield()方法(与操作系统CPU调度有关)具有更好的可移植性。8.Java中有几种实现线程的方法。创建线程的三种方式:继承Thread类实现Runnable接口通过Callable和FutureTask创建线程。Runnable的实现和Callable接口的实现基本相同,只是后者执行的是call()方法,是有返回值的。1、如果需要访问当前线程,必须调用Thread.currentThread()方法。2、继承Thread类的线程类不能再继承其他父类(Java单继承决定)。注意:一般推荐通过实现接口来创建多线程应用。您可以使用Executor框架来创建线程池。Executor框架包括3个部分:(1)Tasks。即工作单元,包括被执行的任务需要实现的接口:Runnable接口或Callable接口;(2)任务的执行。也就是将任务分配给多个线程的执行机制,包括Executor接口和继承自Executor接口的ExecutorService接口。(3)异步计算的结果。包括Future接口和实现Future接口的FutureTask类。9、为什么不推荐使用stop()和suspend()方法不推荐使用stop()方法,因为不安全。它释放线程获取的所有锁,如果对象处于不连贯状态,其他线程可以在该状态下检查和修改它们。事实证明,很难检查出真正的问题出在哪里。suspend()方法容易出现死锁。当调用suspend()时,目标线程将停止,但仍持有之前获得的锁。此时,除非“挂起”的线程恢复,否则没有其他线程可以访问锁定的资源。对于任何线程,如果他们在尝试使用任何锁定资源的同时想要恢复目标线程,则会导致死锁。所以你不应该使用suspend(),但你应该在你的Thread类中放置一个标志来指示线程是活动的还是挂起的。如果标志指示线程应该被挂起,则使用wait()告诉它进入等待状态。如果该标志指示线程应该恢复,则线程将通过notify()重新启动。10、启动线程,使用run()或start()启动线程就是调用start()方法使线程所代表的虚拟处理器处于可运行状态,即可以被调度执行由JVM。这并不意味着线程将立即运行。run()方法可以生成一个必须退出标志来停止线程。11、内部类实现了4个线程,其中两个线程每次j加1,另外两个线程每次j减1publicclassThreadTest1{privateintj;publicstaticvoidmain(Stringargs[]){ThreadTest1tt=newThreadTest1();Incinc=tt.newInc();Decdec=tt.newDec();for(inti=0;i<2;i++){线程t=新线程(inc);t.开始();t=新线程(dec);t.开始();}}privatesynchronizedvoidinc(){j++;System.out.println(Thread.currentThread().getName()+"-inc:"+j);}privatesynchronizedvoiddec(){j--;System.out.println(Thread.currentThread().getName()+"-dec:"+j);}类Inc实现Runnable{publicvoidrun(){for(inti=0;i<100;i++){inc();}}}类Dec实现Runnable{publicvoidrun(){for(inti=0;i<100;i++){dec();}}}}12.sleep()和wait()有什么区别sleep是线程类(Thread)的方法,导致本线程暂停执行指定时间,给其他线程执行机会,但仍保持监听状态,到时间后会自动恢复调用sleep而不释放对象锁定。wait是Object类的一个方法。调用这个对象的wait方法会导致线程放弃对象锁,进入等待这个对象的等待锁池。只有对该对象发出notify方法(或notifyAll)后,线程才会进入对象锁池,准备获取对象锁,进入运行状态。13.监视器(Monitor)监视器和锁在Java虚拟机中是一起使用的。监视器监视同步代码块以确保一次只有一个线程执行同步代码块。每个监视器都与一个对象引用相关联。在获得锁之前,不允许线程执行同步代码。14、同步方式和同步码块的区别。同步方法默认使用this或当前类对象作为锁;同步代码块可以选择锁定什么,比同步方式更细粒度。我们可以只选择同步会发生同步问题的部分代码而不是整个方法。15、线程从创建到死亡的几种状态新建(new):新创建一个线程对象。可运行(runnable):线程对象创建后,其他线程(如主线程)调用该对象的start()方法。该状态的线程位于runnable线程池中,等待被线程调度选中,获得cpu的使用权。运行中(running):处于可运行状态(runnable)的线程已经获得cpu时间片(timeslice)并执行程序代码。阻塞(block):阻塞状态是指线程由于某种原因放弃了cpu的使用权,即放弃了cpu时间片,暂时停止运行。直到线程进入runnable状态,才有机会再次获取cputimeslice,进入running状态。阻塞分为三种:等待阻塞:正在运行(running)的线程执行o。wait()方法,JVM会将线程放入等待队列(waitingqueue)。同步阻塞:当运行线程获取对象的同步锁时,如果同步锁被其他线程占用,JVM会将线程放入锁池(lockpool)。其他阻塞:当一个正在运行的(running)线程执行Thread时。睡眠(长毫秒)或t。join()方法,或发出I/O请求,JVM会将线程置于阻塞状态。当sleep()状态超时,join()等待线程终止或超时,或者当I/O处理完成后,线程重新转入可运行(runnable)状态。死亡(dead):当线程run()和main()方法执行结束,或run()方法异常退出时,线程结束其生命周期。死线程不能再次复活。16、Java多线程回调所谓回调就是客户端程序C调用服务程序S中的某个方法A,然后S又在某个时候调用C中的某个方法B。对于C来说,这个B被称为回调方法。17.启动线程有哪几种方式?第一种:继承Thread类创建线程类定义Thread类的子类,并重写该类的run方法。run方法的方法体表示线程要完成的任务。因此,run()方法被称为执行体。创建Thread子类的实例意味着创建一个线程对象。调用线程对象的start()方法启动线程。公共类FirstThreadTest扩展线程{inti=0;//重写run方法,run方法的方法体为现场执行体publicvoidrun(){for(;i<100;i++){System.out.println(getName()+""+我);}}publicstaticvoidmain(String[]args){for(inti=0;i<100;i++){System.out.println(Thread.currentThread().getName()+":"+i);if(i==20){newFirstThreadTest().start();newFirstThreadTest().start();}}}}上面代码中的Thread.currentThread()方法返回当前正在执行的线程对象。GetName()方法返回调用该方法的线程的名称。第二种方法:通过Runnable接口创建线程类定义runnable接口的实现类,重写接口的run()方法。run()方法的方法体也是线程的线程执行体。创建一个Runnable实现类的实例,并以这个实例作为Thread的目标来创建一个Thread对象,这才是真正的线程对象。调用线程对象的start()方法启动线程。公共类RunnableThreadTest实现Runnable{privateinti;publicvoidrun(){for(i=0;i<100;i++){System.out.println(Thread.currentThread().getName()+""+i);}}publicstaticvoidmain(String[]args){for(inti=0;i<100;i++){System.out.println(Thread.currentThread().getName()+""+i);if(i==20){RunnableThreadTestrtt=newRunnableThreadTest();newThread(rtt,"新线程1").start();newThread(rtt,"新线程2").start();}}}}第三:创建线程通过Callable和Future创建Callable接口的实现类,实现call()方法。call()方法会作为线程执行体,有返回值。创建Callable实现类的实例,使用FutureTask类封装Callable对象,FutureTask对象封装了Callable对象的call()方法的返回值。使用FutureTask对象作为Thread对象的目标创建并启动一个新线程。调用FutureTask对象的get()方法获取子线程执行后的返回值。导入java.util.concurrent.Callable;导入java.util.concurrent.ExecutionException;导入java.util.concurrent.FutureTask;公共类CallableThreadTest实现Callable
