Java线程(线程)
线程与进程的区别
进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。
一个程序至少有一个进程,一个进程至少有一个线程。
创建线程的三种方式
继承Thread类
- 定义Thread类的子类,重写该类的run方法。
-
创建Thread子类的实例,即创建了线程对象(new project)
-
调用线程对象的start()方法来启动该线程(project.start();)
实现Runnable接口
-
定义runnable接口的实现类,并重写该接口的run方法
-
创建该实现类的实例,并以该实例作为Thread的target来创建Thread对象(
Thread t1 = new Thread(myThreandImpl)
),这个thread对象才是真正的线程对象 -
调用线程对象的start()方法来启动该线程
实现Callable接口
-
创建Callable接口的实现类,并实现call()方法,该call方法将作为线程执行体,并且有返回值。
-
创建Callable实现类的实例对象,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call方法的返回值。
-
使用FutureTask对象作为Thread对象的Target,创建并启动新线程
-
调用TutureTask对象的get()方法来获得子线程执行结束后的返回值。
三种创建线程的优缺点
(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
(2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。
-
采用实现Runnable接口方式:
(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
(2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
-
Runnable和Callable的区别:
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
start()和run()方法的区别
线程生命周期
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。 在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞 (Blocked)和死亡(Dead)5 种状态。
尤其是当线程启动以后,它不可能一直"霸占"着 CPU 独自 运行,所以 CPU 需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。
线程状态之新建状态New
当程序使用 new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配 内存,并初始化其成员变量的值。
线程状态之就绪状态Runnable
当线程对象调用了 start()方法之后,该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和 程序计数器,等待调度运行。
线程状态之运行状态Running
如果处于就绪状态的线程获得了 CPU,开始执行 run()方法的线程执行体,则该线程处于运行状态。
线程状态之阻塞状态Blocked
阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。 直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状 态。阻塞的情况分三种:
-
等待阻塞:运行状态下(Running)的线程执行o.wait()方法,JVM会把该线程放入等待队列中(waitting queue)
-
同步阻塞:运行状态下(Running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中
-
其他阻塞:运行状态下(Running)的线程执行Thread.sleep()或t.join()方法,或者发出了I/O请求时,,JVM会把该线程置为阻塞状态。当sleep()状态超时,join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态
线程状态之死亡状态Dead
线程死亡通常由以下三种方式执行:
-
正常结束:run()或call()方法执行完成,线程正常结束
-
异常结束:线程抛出一个未捕获的Exception或Error
-
调用stop:直接调用该线程的stop()方法,来结束该线程(但是该方法容易造成死锁,不推荐使用)