java疯狂讲义第16章多线程

16.2线程的创建和启动

16.2.1继承Thread类创建线程类

使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。

16.2.2 实现Runnable接口创建线程类

通过继承Thread类来获得当前线程对象比较简单,直接使用this就可以了;
但通过实现Runnable接口来获得当前线程对象,则必须使用Thread.currentThread()方法。

采用Runnable接口的方式创建的多个线程可以共享线程类的实例变量。(这是因为在这种方式下,程序所创建的Runnable对象只是线程的target,而多个线程可以共享同一个target,所以多个线程可以共享同一个线程类(实际上应该是线程的target类)的实例变量)。

16.2.3使用Callable和Future创建线程

Callable接口提供了一个call()方法可以作为线程执行体,但call()方法比run()方法功能更强大。1.calll()方法可以有返回值;2.call()方法可以声明抛出异常。

创建并启动有返回值的线程步骤如下:
1.创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,再创建Callable实现类的实例。
2.使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(使用Future原因:Callable接口不是Runnable接口的子接口,所以Callable对象不能直接作为Thread的target,而且call()方法还有一个返回值,需要Future接口的get()方法)
3.使用FutureTask对象作为Thread对象的target创建并启动新线程。
4.调用FutureTask()对象的get()方法来获得子线程执行结束后的返回值。


16.2.4 创建线程的三种方式对比

1、采用实现Runnable、Callable接口的方式创建多线程时,
优势是:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势是:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
2、使用继承Thread类的方式创建多线程时,
优势是:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势是:
线程类已经继承了Thread类,所以不能再继承其他父类。
3、Runnable和Callable的区别
(1) Callable规定(重写)的方法是call(),Runnable规定(重写)的方法是run()。
(2) Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
(3) call方法可以抛出异常,run方法不可以。
(4) 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

16.3 线程的生命周期

  当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,当线程对象调用了start()方法之后,该线程处于就绪状态。(启动线程用start()方法,而不是run()方法)
不要对处于死亡状态的线程调用start()方法,程序只能对新建状态的线程调用start()方法。

16.4 控制线程

16.4.1 join线程

  Thread提供了一个让一个线程等待另一个线程完成的方法----join()方法。当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。

16.4.3 线程睡眠:sleep

  如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。

16.4.4 线程让步:yield

  yield()方法和sleep()方法有点相似,它也是Thread类提供的静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态。yield()只是让当前线程暂停一下,让系统的线程调度器重新调度一次(根据线程的优先级)。

16.5 线程同步

16.6 线程通信

16.6.1传统的线程通信

Object类提供wait(),notify()和notifyAll()三个方法,这三个方法不属于Thread类。

16.6.2 线程状态

NEW:状态是指线程刚创建, 尚未启动

RUNNABLE:状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等

BLOCKED:这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 或者可重入的 synchronized块里别人调用wait() 方法, 也就是这里是线程在等待进入临界区

WAITING:这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束

TIMED_WAITING:这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态

TERMINATED: 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)

16.8线程池

使用线程池来执行线程任务的步骤如下:
1.调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程池。
2.创建Runnable实现类或Callable实现类的实例,作为线程执行任务。
3.调用ExecutorService对象的submit()方法来提交Runnable实例或Callable实例。
4.当不想提交任何任务时,调用ExecutorService对象的shutdown()方法来关闭线程池。 

原文地址:https://www.cnblogs.com/jingpeng77/p/12464470.html