第 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. Educational Codeforces Round 70 题解

    噩梦场. 题目出奇的难,好像一群外国老哥看 A 看着看着就哭了-- A 找到 \(b\) 最低的 \(1\),这个 \(1\) 肯定要跟 A 中的一个 \(1\) 搭配,而且是能搭配的 \(1\) 中 ...

  2. PLSQL安装过程和SCOTT用户被锁的解决方法

    一.PLSQL安装: PLSQL基本就是一键式安装,没有什么需要修改的东西,一路Next就行了. 二.SCOTT用户被锁问题解决 1.问题如下: SCOTT用户默认是被锁的,需要通过system管理员 ...

  3. C#原型模式(深拷贝、浅拷贝)

    原型模式就是用于创建重复的对象,当想要创建一个新的对象但是开销比较大或者想将对象的当前状态保存下来的时候,我们就可以使用原型模式. 创建原型 public abstract class Base { ...

  4. sync 异步编程

    using System; using System.Net; using System.Threading; using System.Threading.Tasks; namespace Cons ...

  5. 百度前端技术学院-task1.4源代码

    任务描述 实现如 示例图(点击打开) 的效果 灰色元素水平垂直居中,有两个四分之一圆位于其左上角和右下角. 任务注意事项 思考不同情况下(如灰色高度是根据内容动态变化的)水平垂直居中的解决方案. 动手 ...

  6. MySQL如何定位慢sql

    MySQL如何定位慢sql MySQL"慢SQL"定位 数据库调优我个人觉得必须要明白两件事 1.定位问题(你得知道问题出在哪里,要不然从哪里调优呢) 2.解决问题(这个没有基本的 ...

  7. 使用RunTime.getRunTime().addShutdownHook优雅关闭线程池

    有时候我们用到的程序不一定总是在JVM里面驻守,可能调用完就不用了,释放资源. RunTime.getRunTime().addShutdownHook的作用就是在JVM销毁前执行的一个线程.当然这个 ...

  8. winfrom判断程序是否运行,并给提示

    在Program.cs文件中修改为: private static System.Threading.Mutex mutex; /// <summary> /// 应用程序的主入口点. / ...

  9. C++贪心算法实现活动安排问题

    问题描述: 设有n个活动的集合E={1,2,…,n},其中,每个活动都要求使用同一资源,而在同一时间内只有一个活动能使用这一资源.每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且s ...

  10. 使用Git Bash向GitHub上传本地项目

    第一步:下载Git Bash(https://gitforwindows.org/),安装的过程是一路下一步,就不细说啦: 第二步:打开Git Bash,如下图显示: 第三步:现在让我们先放一放Git ...