JAVA 技术手册 卷1 第十四章『多线程』 读书摘要
什么是线程
- 进程受CPU时间片的轮转调度,进而予人多任务并发的感觉。
- 线程在更低层次上扩展多任务概念,一个进程通常包含多个线程。
- 进程各自数据独立,而线程共享数据。
- 数据独立使进程相互通信变得繁难,共享数据又使线程并发暗藏风险。
创建线程的两种方式:
- 创建一个任务,受单独线程调度。
public class Task implements Runnable {
public void run() {
// TODO: 具体的执行
}
public static void main(String[] args) {
Runnable runnable = new Task();
Thread t = new Thread(runnable);
t.start();
}
}
- 创建一个Thread类的子类,不推荐该方式。
public class MyThread extends Thread {
@Override
public void run() {
// TODO: 具体的执行任务
}
}
中断线程
- 可以用
interrupt
方法请求中止线程,线程的中断标志被置位,线程某些情况下应不时检查该标志while (!Thread.currentThread().isInterrupted()) {
// TODO: 接着做
}
- 如果线程阻塞(调用
sleep
或wait
),被interrupt
的线程会抛出InterruptedException
,不可中断的阻塞不会响应interrupt
- 如果已被
interrupt
的线程调用sleep
,将会复位中断标志并抛出InterruptedException
- Thread有两个非常类似的方法,
interrupted
与isInterrupted
。interrupted
是静态方法,会复位当前线程的中断标志,isInterrupted
是实例方法,不改变中断标志
线程状态
线程一共含有6种状态,记于java.lang.Thread.State:
- New
- Runnable
- Blocked
- Waiting
- Timed waiting
- Terminated
线程不会一直保持运行。
抢占式调度系统在分配时间片时赋予线程运行权,时间片用完时剥夺线程运行权。
选择下一线程时会考虑线程的优先级。
Blocked、Waiting、Timed waiting:
- 当前线程试图获取对象锁而不得时,进入Blocked状态。当其他线程释放该锁,且当前线程持有该锁进入非Blocked状态。
- 当线程等待java.util.concurrent库中
Lock
或Condition
、调用Object.wait
、Thread.join
等方法时,进入Waiting状态。此时当前线程在等待另一线程通知线程调度器。 - 当线程调用
Thread.sleep
、Object.wait
、Thread.join
、Lock.tryLock
、Condition.await
等待时间参数的超期方法,进入Timed waiting状态。 - 当一个线程被重新激活时,线程调度器会检查其优先级是否比已在运行的线程优先级高,若是,则挑一个线程剥夺其运行权,选择被激活线程运行。
线程退出
- run方法退出而线程退出
- 因未捕获异常抛出而线程退出
线程属性
优先级
- 每个线程都有优先级。
- 默认情况下,每个线程继承父线程的优先级。
- 可以调用
Thread.setPriority
设置线程的优先级 - JVM的线程优先级高度依赖于系统,Java线程的优先级被映射到宿主平台的优先级上。
- Windows上有7个优先级,Java线程优先级会出现多对一的情况。
- Linux上线程不具有优先级。
守护线程
- 线程启动前调用
Thread.setDaemon(true)
将线程转换为守护线程 - 当只剩下守护线程时,JVM退出
Unchecked Exception处理器
- 调用
Thread.setUncaughtExceptionHandler
为线程安装异常处理器 - 调用静态
Thread.setDefaultUncaughtExceptionHandler
为所有线程安装默认异常处理器 - 如果不为线程安装异常处理器,默认处理器为该线程的
ThreadGroup
对象 ThreadGroup
的uncaughtException
的处理逻辑如下:- 父
ThreadGroup
DefaultUncaughtExceptionHandler
Throwable
不是ThreadDeath
实例,线程名字以及Throwable的栈信息输出到System.err
- 父
- 使用Unchecked Exception处理器目的在于健壮线程异常退出的记录,如需恢复当前任务只能新建线程重新执行
线程同步
java.util.concurrent.locks.ReentrantLock
- 示例代码
private Lock lock = new ReentrantLock(); public void doSomething() {
lock.lock();
try {
// do something
} finally {
lock.unlock();
}
}
- ReentrantLock 可重入,锁内部有计数机制
- 构造器
ReentrantLock(boolean fair)
构建公平策略锁,以牺牲性能的代价偏爱等待时间最长的线程。无法保证线程调度器的绝对公平。
java.util.concurrent.locks.Condition
- 一个锁可以含有一个或多个条件对象
- 调用
java.util.concurrent.locks.Lock#newCondition
获得一个条件对象 - 线程在持有锁的情况下调用
java.util.concurrent.locks.Condition#await()
放弃锁并阻塞在Condition
的等待集上 - 另一线程
java.util.concurrent.locks.Condition#signalAll
激活因条件对象阻塞的线程,这些线程从等待集中移出,再次接受线程调度器的调度,试图重入锁对象。一旦获得锁对象,将从await
调用地方返回并继续执行 - 示例代码
while(!(ok to proceed))
condition.await();
synchronized
- 每个JAVA对象都有一个内部锁
- 对象内部锁只有一个
Condition
- 对象内部锁的
wait
与notifyAll
,等价于Condition
的await
与signalAll
synchronized
方法锁对象synchronized
静态方法锁对象的类
Volatile
- 不同线程访问同一内存地址可能获得不同的值,因为寄存器或本地缓冲区的存在
- 编译器可以改变指令执行的顺序而不改变代码的语义
- 使用锁不必担心数据访问不一致,锁保护的临界区不能重排序指令,必要时刷新本地缓存
- 将域声明为
volatile
保证数据访问一致性,但不保证数据修改的原子性
final
- final修饰的域保证多线程下数据访问一致性
Atomic
java.util.concurrent.atomic
下提供了诸如AtomicInteger
、AtomicReference
等原子类Atomic
既保证数据访问一致性,同时保证了数据修改的原子性
ThreadLocal
- 每个线程拿到的变量值都是特属于自己的副本,故没有同步的代价
java.util.Random
是线程安全的,为提高性能JAVA 7引入了java.util.concurrent.ThreadLocalRandom
java.text.SimpleDateFormat
是非线程安全的,解决该问题为每一个线程构造一个实例,示例代码如下public static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
Lock's wait and interrupt
java.util.concurrent.locks.Lock#lock
不能被中断,如果线程在等待获得锁时被中断,被中断线程在获得锁之前一直处于阻塞状态java.util.concurrent.locks.Lock#tryLock(long, java.util.concurrent.TimeUnit)
可以被中断,中断将抛出InterruptedException
java.util.concurrent.locks.Condition#await(long, java.util.concurrent.TimeUnit)
与tryLock
类似
write/read Lock
java.util.concurrent.locks.ReentrantReadWriteLock
读并发,写互斥readLock
获取读锁,多个读操作可重入,排斥写锁。writeLock
获取写锁,排斥其他所有锁。
阻塞队列
队列方法
add
添加一个元素,如果队列满则抛错element
返回队列的头元素,如果队列空则抛错offer
添加一个元素返回true,如果队列满则返回falsepeek
返回队列头元素,如果队列空,返回nullpoll
移出并返回队列头元素,如果队列空,返回nullput
添加一个元素,如果队列满则阻塞remove
移出并返回队列头元素,如果空则抛错take
移出并返回队列头元素,如果空则阻塞
七种队列
ArrayBlockingQueue
:一个由数组结构组成的有界阻塞队列。LinkedBlockingQueue
:一个由链表结构组成的有界阻塞队列。PriorityBlockingQueue
:一个支持优先级排序的无界阻塞队列。DelayQueue
:一个使用优先级队列实现的无界阻塞队列。SynchronousQueue
:一个不存储元素的阻塞队列。LinkedTransferQueue
:一个由链表结构组成的无界阻塞队列。LinkedBlockingDeque
:一个由链表结构组成的双向阻塞队列。
参考资料
JAVA 技术手册 卷1 第十四章『多线程』 读书摘要的更多相关文章
- 《Java并发编程实战》第十四章 构建自己定义的同步工具 读书笔记
一.状态依赖性的管理 有界缓存实现的基类 @ ThreadSafe public abstract class BaseBoundedBuffer<E> { @GuardeBy( &quo ...
- 《Java并发编程实战》第十四章 构建自己的同步工具定义 札记
一.状态依赖性的管理 有界缓存实现的基类 @ ThreadSafe public abstract class BaseBoundedBuffer<E> { @GuardeBy( &quo ...
- java并发编程实战:第十四章----构建自定义的同步工具
一.状态依赖性管理 对于单线程程序,某个条件为假,那么这个条件将永远无法成真 在并发程序中,基于状态的条件可能会由于其他线程的操作而改变 可阻塞的状态依赖操作的结构 acquire lock on o ...
- 《Java编程思想》笔记 第十四章 类型信息
1.RTTI:在运行时识别一个对象类型 JAVA在运行时 有时要 识别对象和类的信息这个机制叫RTTI.Java提供了两种机制去做这件事.传统的RTTI 和 反射. 传统的RTTI 假定编译时就已经 ...
- 《Python 学习手册4th》 第十四章 迭代器和解析
''' 时间: 9月5日 - 9月30日 要求: 1. 书本内容总结归纳,整理在博客园笔记上传 2. 完成所有课后习题 注:“#” 后加的是备注内容 (每天看42页内容,可以保证月底看完此书) “重点 ...
- 《Java并发编程实战》第十二章 测试并发程序 读书笔记
并发测试分为两类:安全性测试(无论错误的行为不会发生)而活性测试(会发生). 安全測试 - 通常採用測试不变性条件的形式,即推断某个类的行为是否与其它规范保持一致. 活跃性測试 - 包含进展測试和无进 ...
- “全栈2019”Java多线程第三十四章:超时自动唤醒被等待的线程
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第二十四章:等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第十四章:线程与堆栈详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
随机推荐
- 【强烈推荐】XCODE的插件之王
有许多关于Xcode的插件,在这里强烈推荐的是Alcatraz插件.因为我们可以通过这个插件来安装其他插件 1.Alcatraz插件. Alcatraz是一个方便我们安装各种那个插件的插件.插件之王? ...
- Starling中通过PivotX 和 PivotY 修改原点
一个显示对象的默认原点在左上角.addChild 是将它的左上角放在了父容器的(0, 0)位置. 如果将该显示对象的PivotX 和 PivotY 修改为其宽高的一半,那么它的原点就变到了该对象的中心 ...
- Ubuntu下安装中文输入法
搜狗输入法 for Linux 是基于Fcitx 框架(fcitx-sogoupinyin). 安装环境为Ubuntu 13.04 安装过程: 卸载Ubuntu默认的ibus输入法: sudo apt ...
- Windows 10 IoT Serials 2 - Windows 10 IoT RTM 升级教程
7月29日,微软推出了Windows 10 for PC的正式版,其版本号是Build 10240.近两天官方说已经有4700万的下载安装量,同时这个数字还在不断攀升.另外,除了Windows 10 ...
- 显示转换explicit和隐式转换implicit
用户自定义的显示转换和隐式转换 显式转换implicit关键字告诉编译器,在源代码中不必做显示的转型就可以产生调用转换操作符方法的代码. 隐式转换implicit关键字告诉编译器只有当源代码中指定了显 ...
- sp_addlinkedserver '(null)' is an invalid product name
使用SSMS 2008客户端工具逆向生成了创建链接服务器的脚本时,在测试环境执行是报如下错误:'(null)' is an invalid product name. USE [master] GO ...
- JavaScript中function的多义性
JavaScript 中的 function 有多重意义.它可能是一个构造器(constructor),承担起对象模板的作用: 可能是对象的方法(method),负责向对象发送消息.还可能是函数,没错 ...
- Keystone 命令汇总
Keystone 命令汇总 目录 [隐藏] 1 用户(User) 1.1 查看用户列表 1.2 创建用户 1.3 删除用户 1.4 显示用户详细信息 1.5 更新用户的密码 1.6 赋予用户一个角 ...
- JQuery判断元素是否存在
JQuery判断元素是否存在的原理与javascript略有不同,因为$选择器选择的元素无论是否存在都不会返回null或undefined,要使用JQuery判断元素是否存在,只能使用length属性 ...
- ASP.NET全局错误处理和异常日志记录以及IIS配置自定义错误页面
应用场景和使用目的 很多时候,我们在访问页面的时候,由于程序异常.系统崩溃会导致出现黄页.在通常的情况下,黄页对于我们来说,帮助是极大的,因为它可以帮助我们知道问题根源,甚至是哪一行代码出现了错误.但 ...