第 1 章 Java 多线程技能

本章主要内容

线程的启动
如何使线程暂停
如何使线程停止
线程的优先级
线程安全相关的问题

1.1 进程和多线程的概念及线程的优点

  进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。进程是操作系统管理的基本运行单元。

  那什么是线程呢?线程可以理解成是在进程中独立运行的子任务。

  使用多线程技术后,可以在同一时间内运行更多不同种类的任务。

1.2 使用多线程

  一个进程正在运行时至少会有 1 个线程在运行。

1.2.1 继承 Thread 类

  实现多线程编程的方式主要有两种:一种是继承 Thread 类,另一种是实现 Runnable 接口。

  Thread 类实现了 Runnable 接口,它们之间具有多态关系。

  线程是一个子任务,CPU 以不确定的方式,或者说是以随机的时间来调用线程中的 run 方法。

  Thread.java 类中的 start() 方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的 run() 方法。这个过程其实就是让系统安排一个时间来调用 Thread 中的 run() 方法,也就是使线程得到运行,启动线程,具有异步执行的效果。如果调用代码 thread.run() 就不是异步执行了,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由 main 主线程来调用 run() 方法,也就是必须等 run() 方法中的代码执行完后才可以执行后面的代码。

  执行 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 的过程中,会出现一些意想不到的情况,其实这也是多线程具有不可预知性的一个体现。

Java多线程编程核心技术-第1章-Java多线程技能-读书笔记的更多相关文章

  1. Java多线程编程核心技术-第4章-Lock的使用-读书笔记

    第 4 章 Lock 的使用 本章主要内容 ReentrantLocal 类的使用. ReentrantReadWriteLock 类的使用. 4.1 使用 ReentrantLock 类 在 Jav ...

  2. Java多线程编程核心技术-第3章-线程间通信-读书笔记

    第 3 章 线程间通信 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大 ...

  3. Java编程思想——第17章 容器深入研究 读书笔记(三)

    七.队列 排队,先进先出. 除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: ad ...

  4. Java并发编程实战 第16章 Java内存模型

    什么是内存模型 JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见. JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Be ...

  5. Java多线程编程核心技术-第7章-拾遗增补-读书笔记

    第 7 章 拾遗增补 本章主要内容 线程组的使用. 如何切换线程状态. SimpleDataFormat 类与多线程的解决办法. 如何处理线程的异常. 7.1 线程的状态 线程对象在不同的运行时期有不 ...

  6. Java多线程编程核心技术-第5章-定时器 Timer-读书笔记

    第 5 章 定时器 Timer 定时 / 计划功能在移动开发领域使用较多,比如 Android 技术.定时计划任务功能在 Java 中主要使用的就是 Timer 对象,他在内部使用多线程的方式进行处理 ...

  7. Java多线程编程核心技术-第2章-对象及变量的并发访问-读书笔记

    第 2 章 对象及变量的并发访问 本章主要内容 synchronized 对象监视器为 Object 时的使用. synchronized 对象监视器为 Class 时的使用. 非线程安全是如何出现的 ...

  8. java多线程编程核心技术——第三章

    第一节等待/通知机制 1.1不使用等待/通知机制实现线程间的通讯 1.2什么是等待/通知机制 1.3等待/通知机制的实现 1.4方法wait()锁释放与notify()锁不释放 1.5当interru ...

  9. java多线程编程核心技术——第四章总结

    第一节使用ReentrantLock类 1.1使用ReentrantLock实现同步:测试1 1.2使用ReentrantLock实现同步:测试2 1.3使用Condition实现等待/同步错误用法与 ...

随机推荐

  1. BBS 03day

    目录 BBS_03 day: 自定义标签 过滤器: 文章的点赞,点彩功能: 文章的评论功能 transaction用法: 自定义 标签代码展示: BBS_03 day: 自定义标签 过滤器: --&g ...

  2. [LeetCode] 738. Monotone Increasing Digits 单调递增数字

    Given a non-negative integer N, find the largest number that is less than or equal to N with monoton ...

  3. Gogs配置(本地安装篇-Debian)

    知识储备: 用过MySQL等 了解Linux最基本的操作 git常用操作 关于ssh 本文参考:linux上安装gogs搭建个人仓库 下载 https://github.com/gogs/gogs/r ...

  4. 团队作业第五次—项目冲刺-Day7

    Day7 part1-SCRUM: 项目相关 作业相关 具体描述 所属班级 2019秋福大软件工程实践Z班 作业要求 团队作业第五次-项目冲刺 作业正文 hunter--冲刺集合 团队名称 hunte ...

  5. 论文阅读: VITAMIN-E: Extremely Dense Feature Points

    Abstract propose了一种非直接法叫"VITAMIN-E": 准确而鲁邦, 跟踪的是稠密特征. 传统非直接法对于重建稠密几何有难度因为他们对于点的选择(为了匹配)很慎重 ...

  6. 【学习笔记】字符串—马拉车(Manacher)

    [学习笔记]字符串-马拉车(Manacher) 一:[前言] 马拉车用于求解连续回文子串问题,效率极高. 其核心思想与 \(kmp\) 类似:继承. --引自 \(yyx\) 学姐 二:[算法原理] ...

  7. 使“Cmder Here”菜单在Tab页开新窗口

    Cmder是一个非常好用的的控制台命令行,我们在实际使用的时候,经常通过如下指令将其注册到右键菜单: Cmder.exe /REGISTER ALL 这样就可以在任意文件夹下快速打开Cmder,并且能 ...

  8. STM32最小系统设计

    STM32最小系统设计 概述 最近在在设计一块板子的时候发现在设计STM32电路这部分时,有些东西模棱两可.本着科学严谨的态度,本着对工作负责的态度(板子设计坏了都是money!),这里对STM32最 ...

  9. rabbitMQ基础应用

    1.安装erlang [root@localhost ~]#yum -y install erlang 2.安装rabbitMQ [root@localhost ~]#yum -y install r ...

  10. 2019 大众书网Java面试笔试题 (含面试题解析)

      本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.大众书网等公司offer,岗位是Java后端开发,因为发展原因最终选择去了大众书网,入职一年时间了,也成为了面 ...