什么是线程?
线程是指程序在执行过程中,能够执行程序代码的一个执行单元,在Java语言中,线程有四种状态:运行,就绪,挂起,结束。
线程与进程的区别?
进程是一段正在运行的程序,而线程有时也被称为轻量级进程,它是进程的执行单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间,但是,各个线程拥有自己的栈空间。
为什么使用多线程?
(1)、使用多线程可以减少程序的响应时间。单线程如果遇到等待或阻塞,将会导致程序不响应鼠标键盘等操作,使用多线程可以解决此问题,增强程序的交互性。
(2)、与进程相比,线程的创建和切换开销更小,因为线程共享代码段、数据段等内存空间。
(3)、多核CPU,多核计算机本身就具有执行多线程的能力,如果使用单个线程,将无法重复利用计算资源,造成资源的巨大浪费。
(4)、多线程可以简化程序的结构,使程序便于维护,一个非常复杂的进程可以分为多个线程执行。
多线程出现的原因:
为了解决负载均衡问题,充分利用CPU资源.为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰.为了处理大量的IO操作时或处理的情况需要花费大量的时间等等,比如:读写文件,视频图像的采集,处理,显示,保存等
多线程的好处:
1.使用线程可以把占据时间长的程序中的任务放到后台去处理
2.用户界面更加吸引人,这样比如用户点击了一个按钮去触发某件事件的处理,可以弹出一个进度条来显示处理的进度
3.程序的运行效率可能会提高
4.在一些等待的任务实现上如用户输入,文件读取和网络收发数据等,线程就比较有用了.
多线程的缺点:
1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换.
2.更多的线程需要更多的内存空间
3.线程中止需要考虑对程序运行的影响.
4.通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生
顺序执行流:单个线程的程序只有一个顺序执行流,多线程的程序则可以包含多个顺序执行流,多个顺序执行流之间互不干扰。
一个任务通常就是一个程序,每个运行中的程序就是一个进程。当程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。
进程和线程:运行中的任务,即当一个程序进入内存时,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。线程是进程的组成部分,一个进程可包含多个线程,一个线程必须有一个你进程。线程拥有自己的堆栈,程序计数器,局部变量,但不拥有系统资源。它与父进程的其他线程共享此进程所拥有的全部资源。线程共享的环境包括:进程代码块,进程的公有数据等。利用共享的数据,线程很容易实现相互之间的通信。
进程三个特征:独立性,动态性,并发性。
并发性与并行性:并发性,同一时刻,有多条指令在多个处理器上执行。并发性,同一时刻只有一条指令执行,但多个进程指令被快速轮换执行。
多线程编程的优势:
1、进程之间不能共享内存,线程之间容易共享内存。
2、系统创建进程时需要为该进程重新分配系统资源,但创建线程时则代价小得多,因此通过多线程来实现多任务并发比多进程的效率高。
3、Java语言内置了多线程功能支持。
实际应用:
浏览器能同时下载多个图片;Web服务器响应多个用户响应;Java虚拟机提供了超级进程来进行垃圾回收。。。
线程的创建和启动
1、继承Thread类创建线程类
Java程序运行后,程序至少创建一个主线程,主线程的线程执行休是由main()确定的——main方法的方法体代表主线程的线程执行体。使用Thread类方法创建线程时,多个线程之间无法共享线程类的实例变量。
2、实现Runnable接口创建线程类
Runnable对象仅作为Thread对象的target,而实际的线程对象依然是Thread实例,只是该Thread线程负责执行target的run()方法。采用Runable接口的方式创建的多个线程可以共享线程类的实例属性。因为在这种方式下,程序所创建的Runable对象只是线程的target,而多个线程可以共享一个target。所以多个线程可以共享一个线程类的实例属性。
3、使用Callable接口和Future创建线程
Callable接口提供了一个call()方法可以作为线程执行休,call()方法,可有返回值,可声明抛出异常。Callable对象不能直接作为Thread的target。
Future接口来代表Callable接口里call()的返回值。FutureTask为Future的一个实现类,并实现了Runnable接口——可以作为Thread的target。
三、线程的生命周期
线程的五个状态:新建New,就绪Runnable,运行Running,阻塞Blocked,死亡Dead。
新建:用new关键字创建一个线程时
就绪:调用start()方法后,Java虚拟机会为其创建方法调用栈和程序计数器,何时执行,取决于JVM里线程调试器的调度。相当于”等待执行“。如果希望调用子线程的start()方法后子线程立即开始执行,程序可以使用Thread.sleep(1)方法让当前运行的线程(主线程)睡眠一毫秒,则处于就绪状态的子线程将立即开始执行。
运行:处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。
阻塞:线程运行后,不可能一直处于运行状态,除非它线程执行体足够短,瞬间执行体结束了。线程在运行过程中需要被中断(抢占式调试策略,协作工调试策略),目前的是使其他线程获得执行的机会。线程调试的细节取决于底层平台所采用的策略。
进入阻塞状态的情况以及解除阻塞的情况,以及状态转移图如下:
死亡:run(),call()方法执行完成后,线程抛出一个未捕获的异常,stop()方法。isAlive()方法,当线程处于,就绪,运行,阻塞状态时,返回true;新建,死亡时,返回false。
Executors类是什么
Executors为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供了一些工具方法。
Executors可以用于方便的创建线程池。
什么是FutureTask?
FutureTask是Future的一个基础实现,我们可以将它同Executors使用处理异步任务。通常我们不需要使用FutureTask类,单当我们打算重写Future接口的一些方法并保持原来基础的实现是,它就变得非常有用。我们可以仅仅继承于它并重写我们需要的方法。
用户线程和守护线程有什么区别?
当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。
线程生命周期?
新建、就绪、运行(活动)、阻塞和死亡。
创建并运行线程:
① 新建状态(New Thread):在Java语言中使用new 操作符创建一个线程后,该线程仅仅是一个空对象,它具备类线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态。Thread t1=new Thread();
线程处于创建状态时,可通过Thread类的方法来设置各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。
② 就绪状态(Runnable):使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,即正在等待被分配给CPU时间片,使该线程处于就绪状态,也就是说此时线程正在就绪队列中排队等候得到CPU资源。此外,如果某个线程执行了yield()方法,那么该线程会被暂时剥夺CPU资源,重新进入就绪状态。例如:t1.start();
③ 运行状态(Running):Java运行系统通过调度选中一个处于就绪状态的线程,使其占有CPU并转为运行状态。此时,系统真正执行线程的run()方法,即线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
a) 可以通过Thread类的isAlive方法来判断线程是否处于就绪/运行状态:当线程处于就绪/运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于阻塞状态,也可能处于停止状态。
④ 阻塞和唤醒线程
阻塞状态(Blocked):一个正在运行的线程因某些原因不能继续运行时,就进入阻塞 状态。这些原因包括:
a) 当执行了某个线程对象的sleep()等阻塞类型的方法时,该线程对象会被置入一个阻塞集内,等待超时而自动苏醒。
b) 当多个线程试图进入某个同步区域时,没能进入该同步区域的线程会被置入锁定集,直到获得该同步区域的锁,进入就绪状态。
c) 当线程执行了某个对象的wait()方法时,线程会被置入该对象的等待集中,直到执行了该对象的notify()方法wait()/notify()方法的执行要求线程首先获得该对象的锁。
⑤ 死亡状态(Dead):线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。
终止线程的三种方法
① 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,推荐使用。
② 使用stop方法强制终止线程(这个方法不推荐使用,因为stop和suppend、resume一样,也可能发生不可预料的结果)。
③ 使用interrupt方法中断线程。
常用方法
void run() 创建该类的子类时必须实现的方法
void start() 开启线程的方法
static void sleep(long t) 释放CPU的执行权,不释放锁
static void sleep(long millis,int nanos)
final void wait()释放CPU的执行权,释放锁
final void notify()
static void yied()可以对当前线程进行临时暂停(让线程将资源释放出来)
你对线程优先级的理解是什么?
每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级。
线程调度器(Thread Scheduler)和时间分片(Time Slicing)
线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。
上下文切换
先来解释一下什么是上下文切换(context switch)。在多任务处理系统中,作业数通常大于CPU数。为了让用户觉得这些任务在同时进行,CPU给每个任务分配一定时间,把当前任务状态保存下来,当前运行任务转为就绪(或者挂起、删除)状态,另一个被选定的就绪任务成为当前任务。之后CPU可以回过头再处理之前被挂起任务。上下文切换就是这样一个过程,它允许CPU记录并恢复各种正在运行程序的状态,使它能够完成切换操作。在这个过程中,CPU会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行。
为什么线程通信的方法wait(), notify()和notifyAll()被定义在Object类里?
Java的每个对象中都有一个锁(monitor,也可以成为监视器) 并且wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法
为什么Thread类的sleep()和yield()方法是静态的?
Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。
如何确保线程安全?
在Java中可以有很多方法来保证线程安全——同步,使用原子类(atomic concurrent classes),实现并发锁,使用volatile关键字,使用不变类和线程安全类。
volatile关键字在Java中有什么作用?
当我们使用volatile关键字去修饰变量的时候,所以线程都会直接读取该变量并且不缓存它。这就确保了线程读取到的变量是同内存中是一致的。
同步方法和同步块,哪个是更好的选择?
同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。
如何创建守护线程?
使用Thread类的setDaemon(true)方法可以将线程设置为守护线程,需要注意的是,需要在调用start()方法前调用这个方法,否则会抛出IllegalThreadStateException异常。
并发,同步,异步?
并发:在单核和多核都可存在,就是同一时间有多个可以执行的进程。但是在单核中同一时刻只有一个进程获得CPU,虽然宏观上你认为多个进程都在进行。多个用户争夺同一个资源(这个资源可以是服务器上的日志,可以是执行某一此sql操作,可以使ftp服务器上的某个文件等,又或者是程序中的某一个全局变量,因此我们可以称这种资源为:全局资源);
并行:是指同一时间多个进程在微观上都在真正的执行,这就只有在多核的情况下了。
(如果一台计算机有n个处理器,那么就有n个线程真正同时运行。单CPU计算机是伪并行,按照某种调度算法,多个轮流运行)
其中两种并发关系分别是同步和互斥。
互斥:进程间相互排斥的使用临界资源的现象,就叫互斥。
解释:并发是在多个用户请求同一个资源的时候,或者是程序本身多线程请求同一个资源的时候造成的。
比如:一个财务系统,两个人同时对总钱数进行操作,一个加10块一个减100块,注意这两个操作是同时进行的,那系统就不知道是加还是减了,这是并发问题。或者,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线程的处理的数据,而B线程又修改了A线程处理的数理(线程安全)。
异步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程
仍然请求的到这个资源,A线程无需等待。
同步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求
不到,怎么办,A线程只能等待下去。
同步与异步:显然,同步最安全,最保险的。而异步不安全,容易导致死锁,这样一个线程死掉就会导致整个
进程崩溃,但没有同步机制的存在,性能会有所提升。所以对于同步与异步必须有所取舍。
http://www.cnblogs.com/gongxing/p/4663066.html
http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-answers/
http://www.cnblogs.com/wzy330782/p/5456923.html
http://blog.csdn.net/bieleyang/article/details/76648599
http://blog.csdn.net/lee_sire/article/details/50371962
http://blog.csdn.net/antony9118/article/details/51475034