第 1 章 Java 多线程技能
本章主要内容
线程的启动 如何使线程暂停 如何使线程中止 线程的优先级 线程安全相关的问题
1.1 进程和多线程的概念及线程的优势
进程是操做系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。进程是操做系统管理的基本运行单元。java
那什么是线程呢?线程能够理解成是在进程中独立运行的子任务。编程
使用多线程技术后,能够在同一时间内运行更多不一样种类的任务。安全
1.2 使用多线程
一个进程正在运行时至少会有 1 个线程在运行。多线程
1.2.1 继承 Thread 类
实现多线程编程的方式主要有两种:一种是继承 Thread 类,另外一种是实现 Runnable 接口。异步
Thread 类实现了 Runnable 接口,它们之间具备多态关系。测试
线程是一个子任务,CPU 以不肯定的方式,或者说是以随机的时间来调用线程中的 run 方法。this
Thread.java 类中的 start() 方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的 run() 方法。这个过程其实就是让系统安排一个时间来调用 Thread 中的 run() 方法,也就是使线程获得运行,启动线程,具备异步执行的效果。若是调用代码 thread.run() 就不是异步执行了,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由 main 主线程来调用 run() 方法,也就是必须等 run() 方法中的代码执行完后才能够执行后面的代码。spa
执行 start() 方法的顺序不表明线程启动的顺序。操作系统
1.2.2 实现 Runnable 接口
使用继承 Thread 类的方式来开发多线程应用程序在设计上是有局限性的,由于 Java 是单根继承,不支持多继承,因此为了改变这种限制,可使用实现 Runnable 接口的方式来实现多线程技术。 Thread.java 类也实现了 Runnbale 接口。线程
1.2.3 实例变量与线程安全
自定义线程类中的实例变量针对其余线程能够有共享与不共享之分。
每一个线程都有各自的变量,本身改变本身变量的值,这种状况就是变量不共享。
共享数据的状况就是多个线程能够访问同一个变量。
非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操做时会出现值被更改、值不一样步的状况,进而影响程序的执行流程。
可使用 synchronized 关键字解决非线程安全问题。
1.2.4 留意 i— 与 System.out.println() 的异常
System.out.println("i="+(i--)+" threadName="+Thread.currentThread().getName());
虽然 println() 方法在内部是同步的,可是 i— 的操做倒是在进入 println() 以前发生的,因此有发生非线程安全问题的几率。因此,为了防止发生非线程安全问题,仍是应继续使用同步方法。
1.3 currentThread() 方法
currentThread() 方法可返回代码段正在被哪一个线程调用的信息。
1.4 isAlive() 方法
方法 isAlive() 的功能是判断当前的线程是否处于活动状态。什么是活动状态呢?活动状态就是线程已经启动且还没有终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的。
1.5 sleep() 方法
方法 sleep() 的做用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指 this.currentThread() 返回的线程。
1.6 getId() 方法
getId() 方法的做用是取得线程的惟一标识。
1.7 中止线程
中止一个线程意味着在线程处理完任务以前停掉正在作的操做,也就是放弃当前的操做。中止一个线程可使用 Thread.stop() 方法,但最好不用它。虽然它确实能够中止一个正在运行的线程,可是这个方法是不安全的(unsafe),并且是已被弃用做废的(deprecated),在未来的 Java 版本中,这个方法将不可用或不被支持。
大多数中止一个线程的操做使用 Thread.interrupt() 方法,尽管方法的名称是“中止,停止”的意思,但这个方法不会停止一个正在运行的线程,还须要加入一个判断才能够完成线程的中止。
在 Java 中有如下 3 种方法能够终止正在运行的线程:
1)使用退出标志,使线程正常退出,也就是当 run 方法完成后线程终止。
2)使用 stop 方法强行终止线程,可是不推荐使用这个方法,由于 stop 和 suspend 及 resume 同样,都是做废过时的方法,使用它们可能产生不可预料的结果。
3)使用 interrupt 方法中断线程。
1.7.1 中止不了的线程
调用 interrupt() 方法仅仅是在当前线程中打了一个中止的标记,并非真的中止线程。
1.7.2 判断线程是不是中止状态
在 Java 的 SDK 中,Thread.java 类里提供了两种方法来判断线程的状态是否是中止的。
1)this.interrupted():测试当前线程是否已经中断。
2)this.isInterrupted():测试线程是否已经中断。
interrupted() 方法的解释:测试当前线程是否已经中断。
官方文档对 interrupted 方法的解释:测试当前线程是否已经中断。线程的中断状态由该方法清除。换句话说,若是连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态以后,且第二次调用检验完中断状态前,当前线程再次中断的状况除外)。interrupted() 方法具备清除状态的功能。
方法 isInterrupted() 并未清除状态标志。
this.interrupted():测试当前线程是否已是中断状态,执行后具备将状态清除为 false 的功能。this.isInterrupted():测试线程 Thread 对象是否已是中断状态,但不清除状态标志。
1.7.3 能中止的线程—-异常法
使用 throw new InterruptedException() 方法,在线程中断以后中止中断以后的代码运行。
1.7.4 在沉睡中中止
若是在 sleep 状态下中止某一线程,会进入 catch 语句,而且清除中止状态值,使之变成 false。
1.7.5 能中止的线程—-暴力中止
使用 stop() 方法中止线程则是很是暴力的。
1.7.6 方法 stop() 与 java.lang.ThreadDeath 异常
调用 stop() 方法时会抛出 java.lang.ThreadDeath 异常,但在一般的状况下,此一场不须要显示地捕获。
方法 stop() 已经被做废,由于若是强制让线程中止则有可能使一些清理性的工做得不到完成。另一个状况就是对锁定的对象进行了“解锁”,致使数据得不到同步的处理,出现数据不一致的问题。
1.7.7 释放锁的不良后果
使用 stop() 释放锁将会给数据形成不一致性的结果。若是出现这样的状况,程序处理的数据就有可能遭到破坏,最终致使程序执行的流程错误,必定要特别注意。
因为 stop() 方法已经在 JDK 中被标明使“做废/过时”的方法,显然它在功能上具备缺陷,因此不建议在程序中使用 stop() 方法。
1.7.8 使用 return 中止线程
将方法 interrupted() 与 return 结合使用也能实现中止线程的效果。
在线程中断以后,使用 return 使得没法运行中断以后的代码。
不过仍是建议使用“抛异常”的方法来实现线程的中止,由于在 catch 块中还能够将异常向上抛,使线程中止的事件得以传播。
1.8 暂停线程
暂停线程意味着此线程还能够恢复运行。在 Java 多线程中,可使用 suspend() 方法暂停线程,使用 resume() 方法恢复线程的执行。
1.8.1 suspend 与 resume 方法的使用
suspend() 方法会暂停线程,resume() 方法hi恢复线程的执行。
1.8.2 suspend 与 resume 方法的缺点—-独占
在使用 suspend 与 resume 方法时,若是使用不当,极易形成公共的同步对象的独占,使得其余线程没法访问公共同步对象。
1.8.3 suspend 与 resume 方法的缺点—-不一样步
在使用 suspend 与 resume 方法时也容易出现由于线程的暂停而致使数据不一样步的状况。
1.9 yield 方法
yield() 方法的做用使放弃当前的 CPU 资源,将它让给其余的任务去占用 CPU 执行事件。但放弃的时间不肯定,有可能刚刚放弃,立刻又得到 CPU 时间片。
1.10 线程的优先级
在操做系统中,线程能够划分优先级,优先级较高的线程获得的 CPU 资源较多,也就是 CPU 优先执行优先级较高的线程对象中的任务。
设置线程优先级有助于帮“线程规划器”肯定在下一次选择哪个线程来优先执行。
设置线程的优先级使用 setPriority() 方法。
在 Java 中,线程的优先级分为 1 ~ 10 这 10 个等级,若是小于 1 或大于 10,则 JDK 抛出异常 throw new IllegalArgumentException() 。
JDK 中使用 3 个常量来预置定义优先级的值:
public final static int MIN_PRIORITY = 1; public final static int NORM_PRIORITY = 5; public final static int MAX_PRIORITY = 10;
1.10.1 线程优先级的继承特性
在 Java 中,线程的优先级具备继承性。好比 A 线程启动 B 线程,则 B 线程的优先级与 A 是同样的。
1.10.2 优先级具备规则性
高优先级的线程老是大部分先执行完,但不表明高优先级的线程所有先执行完。线程的哟先机与代码执行顺序无关,线程的优先级具备必定的规则性,也就是 CPU 尽可能将执行资源让给优先级比较高的线程。
1.10.3 优先级具备随机性
线程的优先级还具备“随机性”,也就是优先级较高的线程不必定每一次都先执行完。
不要把线程的优先级与运行结果的顺序做为衡量的标准,优先级较高的线程并不必定每一次都先执行完 run() 方法中的任务,也就是说,线程优先级与打印顺序无关,不要将这二者的关系相关联,它们的关系具备不肯定性和随机性。
1.10.4 看谁运行得快
优先级高的运行的快。
1.11 守护线程
在 Java 线程中有两种线程,一种是用户线程,另外一种是守护现线程。
守护线程是一种特殊的线程,它的特性是“陪伴”的含义,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进行中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。
setDaemon(true)将线程设置为守护线程。
1.12 本章小结
本章介绍了 Thread 类的 API,在使用这些 API 的过程当中,会出现一些意想不到的状况,其实这也是多线程具备不可预知性的一个体现。