前言
最近又仔细钻研了一下Java线程,主要参考了O‘Reilly的《Java线程》一书第二版,我在这里对关键点做一下总结,也算是抛砖引玉吧。(注意:本文不适合初涉线程的Java程序员,需要对线程有初步的理解和实践经验。如果要了解线程的详细内容请阅读《Java线程》,这是一本相当不错的书)
1. 同步
2. 等待和通知
3. 线程调度
4. 多处理器上的多线程
-----------------------------------------------------------------------------------------------
同步
-----------------------------------------------------------------------------------------------
在线程同步技术中非常重要的一个概念是“原子性”,简单讲就是在执行过程中不能被中断的一组操作。
在线程之间共享数据并且对共享数据的操作不是“原子”的就会导致竞态条件(race condition)的产生,竟态条件会造成程序运行错误(或者说数据错误),因此需要对线程进行同步来解决这个问题。
Java提供synchronized关键字,并通过获取对象的互斥锁来实现线程同步。每一个Java对象都会创建一个锁。
Java规范保证对变量进行赋值的操作都是原子性的,除了long和double变量之外。
同步是基于实际对象而不是引用的,不要选择一个可能会在锁的作用域中改变值的实例变量作为锁对象,比如一个可能会被赋值为null的对象引用。通常可以用synchrnized(this)或者建立一个锁对象Object lock = new Object()来实现。
预防死锁:
两个或者多个线程试图获取对方的锁,就会发生死锁的状况。比如某一时刻线程A已获得了锁L1,线程B已获得了锁L2,下一时刻线程A试图获取锁L2,线程B试图获取锁L1,于是两个线程互相等待对方释放锁,产生了死锁。
Java虚拟机本身不会检测死锁,也没有很好的工具可以检测死锁,仔细的检查代码是唯一检测死锁的办法。
为了尽量避免死锁,可以考虑遵循以下原则:
- 最简单的原则:一个同步方法永远不要调用另外一个同步方法。但是这个要求过于严格,且很多时候无法做到,有太多的Java方法是同步的。
- 对与我们要使用的对象相关联但是更高一级的对象加锁。这个原则的问题是扩大了锁的粒度,这与引入多线程的初衷多少有点相违背。
- 避免死锁的最有用原则是:确保以同样的顺序来获取锁。也就是说存在着一种锁层次关系,在做详细设计的时候最好定义一个队列形的锁层次关系,迫使编码按照锁层次的顺序来获取锁。
按顺序获取锁虽然能够有效解决死锁问题,但是却使程序的并行度降低了,在《java线程》一书中定义了一个BusyFlag类用以在预防死锁的同时,提高程序并行度,有兴趣可以参阅。
等待和通知
-----------------------------------------------------------------------------------------------
当线程需要等待某种条件的发生时,使用wait()方法;当某种条件已经发生了,则另外的线程调用notify()方法通知等待的线程。wait()和notify()方法在使用前,必须先获得使用这些方法的对象的锁,否则可能会产生静态条件。如:
synchronized (this) {
...
this.wait();
...
}
wait()方法调用之后,线程进入阻塞状态并暂时释放在该对象上的锁,让其他线程获得锁。
如果有多个线程在等待,那么notify()方法调用的时候会通知到哪个线程呢?Java并没有明确定义哪个线程会收到通知,哪个都有可能,因此可以转而使用notifyAll()方法来通知所有线程。
在《Effective Java》一书中Bloch建议:“永远不要在循环之外调用wait()方法“,这是非常正确的,因为被唤醒时并不意味着等待的条件一定是满足了的,有可能其他线程错误的调用了notify()。其他的可能性参考《Effective Java》。
synchronized (obj) {
while(<条件>) {
obj.wait();
}
......
}
中断线程
Thread类的interrupt()方法可以中断线程的运行,如果被中断线程处于sleep(), wait()阻塞状态,则线程进入非阻塞状态。
但是iterrupt()能否中断I/O阻塞的线程呢?这就不一定了,这取决与虚拟机的实现,不同平台的实现会有不同的结果,比如Windows平台的虚拟机就不能中断。因此不能依赖与这个方法来中断I/O阻塞的线程。正确的方法应该是先关闭该线程阻塞的I/O,在终止线程。
线程调度
-----------------------------------------------------------------------------------------------
在任一时刻,一个CPU上只能运行一个线程,因此Java线程调度就是决定在某一时刻运行哪一个线程。Java规范并没有定义Java虚拟机实现要用什么样的方式来调度线程,只规定调度是基于线程优先级的,但是不同的虚拟机以不同的方式来实现这个方针。
对于程序员来讲,只有当一个或者多个线程在很长一段时间内都是占用CPU较多时才需要考虑Java线程调度。那些运行时间很短的;周期性使用CPU来定时完成任务的;非经常性竞争CPU的线程,都不需要考虑线程调度的问题。
在Java虚拟机中每个线程都处于四种状态之一:
Java虚拟机不会改变线程的优先级,优先级只能由程序员改变。线程调度的基本原则是:当前运行线程应当是所有处于可运行状态的线程中拥有最高优先级的线程。
Java虚拟机实现的调度程序是“抢占”式的,也就是说高优先级的线程会中断正在运行的低优先级线程,以使的高优先级线程成为当前运行线程。
某个线程由于优先级低于其他线程,很难成为当前运行线程,这种那个情况被成为CPU饥饿。Java虚拟机通常不会因为CPU饥饿而调整线程的优先级(有些OS可能会这么做),程序员要保证不会出现CPU饥饿。
在一个常用的Java虚拟机实现方法中,每一个优先级对应有一个链表(假设是链表),初始状态、阻塞状态和退出状态各一个链表。对于某优先级的链表,虚拟机在没有更高优先级的前提下挑选该优先级链表中第一个线程运行,直到有更高优先级的线程需要成为当前运行线程,这个被中断的线程会被移到链表的尾部。等到高优先级的线程重新进入阻塞状态,链表中的下一个线程会成为链表第一个线程,并成为当前运行线程。于是,该优先级的所有线程会随着更高优先级的线程的状态改变而轮流成为当前运行线程。换句话说,同一优先级的线程不会抢占正在运行的其他线程,除非有更高优先级的线程进行干预。
并不是每个Java虚拟机都会按章上面的方式重排链表中的线程,一个实时系统中的线程在被中断后是不会被重新排序的。
具有同样优先级的线程互相抢占的情况称为循环调度(round-robin scheduling),Java规范既没要求也没有禁止虚拟机实现采用这种方式实现,特别是非Windows的虚拟机。
多处理器上的多线程
-----------------------------------------------------------------------------------------------
与单处理器系统不同的是,对于多处理器上的系统来说,可能每一个CPU上都有正在运行的线程。这导致了在单处理器系统中的一些假设不再成立:
- 不能再认定当前运行的线程具有最高的优先级。
- 不能再认定低优先级的线程没法运行。
- 不能再认定不同优先级的线程无法同时运行。
- 有一些单处理器上不会发生的竞态条件,有可能会发生。
附录:
-----------------------------------------------------------------------------------------------
InterruptedException
表明方法比预期时间提前返回
InterruptedIOException
NoSuchMethodError
......
IllegalThreadStateException
IllegalArgumentException
IllegalMonitorStageException
SecurityException
分享到:
相关推荐
### Java线程总结教程知识点详解 #### 一、操作系统与多线程概念 - **多任务与分时操作系统**:现代操作系统(如Windows、Linux)能够实现多任务处理,即在用户看来似乎多个应用程序在“同时”运行。实际上,这是...
总的来说,Java线程总结的知识点涵盖了线程的基本概念、创建与管理、生命周期、同步机制、线程间通信以及线程的活跃性问题。理解和掌握这些知识点对于开发高效、稳定、并发的Java应用程序至关重要。
#### 一、Java线程:概念与原理 - **操作系统中线程和进程的概念** 当前的操作系统通常都是多任务操作系统,多线程是一种实现多任务的方式之一。在操作系统层面,进程指的是内存中运行的应用程序,每个进程拥有...
Java线程是并发编程的核心部分,它允许程序在同一时间执行多个任务,极大地提高了程序的效率和响应速度。在Java中,实现多线程有两种主要方式:继承`Thread`类和实现`Runnable`接口。 1. 继承`Thread`类: 当创建...
JAVA线程总结,包含线程池,显示使用线程实现异步编程,基于JDK中的Future实现异步编程,JDK中的FutureTask等
花费了一上午的时候 写了一些demo。认识到四种线程池的区别。上传到csdn 供以后学习
Java线程是并发编程的核心部分,它允许程序在同一时间处理多个任务。理解Java线程的基础概念和使用方式对于开发高效且响应迅速的应用至关重要。 一、线程与进程 在操作系统中,线程和进程是两个关键概念。进程是...
以下是对Java线程安全的深入总结: ### 一、线程安全的定义 线程安全是指当多个线程访问同一块代码时,如果每个线程都能得到预期的结果,且不产生数据不一致或同步问题,那么这块代码就被称为线程安全的。Java中的...
java中的线程安全,是java中的一个重要知识点,同时也是面试中经常问道的问题
#### 一、Java线程:概念与原理 1. **操作系统中线程和进程的概念** - 当前的操作系统通常为多任务操作系统,多线程是实现多任务的一种手段。 - **进程**:指内存中运行的应用程序,每个进程拥有独立的内存空间。...
Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...
Java多线程是Java编程语言中的一个重要特性,它允许开发者创建并发执行的多个线程,从而提高程序的执行效率和响应速度。Java中实现多线程主要有两种方式:继承Thread类和实现Runnable接口。 ### 继承Thread类 在...
在Java编程语言中,线程是程序执行的基本单元,它允许程序并发地...同时,"Java线程学习和总结.files"目录下的文件可能是与文章相关的辅助资料,例如代码示例或图片。建议结合这些资料一起学习,以获得更全面的知识。
根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...
Java线程状态流转图知识点总结 Java线程状态流转图是一种用于描述Java线程生命周期中不同的状态和状态转换的图形表示方式。该图形展示了Java线程从创建到终止的整个生命周期,并详细介绍了每种状态的特点和转换...
【JAVA多线程总结】 Java 多线程是Java编程中的关键特性,它允许程序同时执行多个任务,提高系统的效率和响应性。本篇总结涵盖了Java多线程的基础概念、创建与启动、线程调度、同步与协作以及新特性。 **一、Java...