线程相关知识
线程的概念
1.线程
(1)线程是由表示程序运行状态的寄存器(包括程序计数器和堆栈)组成的。
(2)线程是程序执行过程中的某一时刻的状态。
(3)线程是一个用户级的实体,在内存中驻留在普通用户级方法可以直接访问的区域。
(4)每个线程都是一个能够独立执行自身指令的控制流程。
(5)操作系统通过对多线程的调度实现线程的并发执行。
(6)线程本身并不是一个程序而是运行于一个程序或者进程中。
(7)线程是个动态的概念,有其自身的产生、存在和消亡的过程。
(8)Java通过在程序中提供多线程支持来提高线程的安全性。
(9)线程是程序中的一个单个执行流,多线程是程序中的多个执行流。
2.程序、线程和进程
(1)程序是一段静态的代码、是应用软件执行的蓝本。
(2)进程是程序的一次动态执行过程,包括程序加载、执行到结束。
(3)线程是比进程更小的执行单位,一个进程可以包含多个线程。
(4)进程是由代码、数据、内核状态和一组寄存器组成的。
(5)线程不包含进程的地址空间中的代码和数据。
考点:2
Java语言中的线程
(1)Java语言中的线程包括3部分:虚拟CPU、该CPU执行的代码,以及代码所操作的数据。
(2) Java语言中代码和数据相互独立,代码可以在不同的线程之间共享,同样数据也可以在不同的线程之间共享。
(3) java.lang.Thread类定义了Java语言中的线程模型,用户可以通过该类创建、定义和控制自己的线程。
8.2线程的创建
考点:3
线程的创建
在Java中,通过调用Thread类的构造方法来创建线程对象,线程执行的入口方法是run(),可以有两种提供run()方法的实现方式。
1.通过实现Runnable接口
创建过程Thread类使用一个Runnable的实例作为其构造方法,该实例对象提供了线程体run(),线程从该run()方法开始执行。新建的线程必须通过调用线程的start ()方法才能运行。
2.通过继承Thread类
Thread类本身实现了Runnable接口,因此Thread类含有run()方法。通过继承Thread类,并且重写其run()方法,来创建线程。
以上两种方法的比较如下:
(1)实现Runnable接口的方法符合面向对象的思想,实现了Runnable接口的类,可以很容易地继承其他的类。一般都提倡使用这种方法。
(2)继承Thread类的方法,比较简单,可以直接调用线程的方法。可以视具体应用而定。
8.3线程的调度与线程控制
考点:4
线程优先级和调度策略
线程的优先级,在Thread类中提供了3个静态常量:MIN_PRIORITY = 1、MAX_PRIORITY = 10、NORM_PRIORITY = 5来控制线程的优先级,数值越大优先级越高。子线程继承父线程的优先级,主线程具有普通优先级。可以通过getPriority()方法获取线程的优先级,通过setPriority()设定线程的优先级。
线程调度是指:在单个CPU上以一定策略控制多个线程的执行,这种策略是抢占式调度,简单地说,就是指高优先级的线程首先运行,低优先级的线程被高优先级线程抢占执行。在Java中,系统按照优先级的不同设置不同的线程等待池,首先运行高优先级等待池的线程,然后再运行低优先级等待池的线程。
考点:5
线程的控制
要控制线程,可以使用Thread类提供的如下方法:
(1)sleep(),一个线程通过调用此方法暂停运行线程一定时间
(2)yield(),一个线程通过调用此方法使与其具有相同的优先级的线程有运行的机会。
(3)join(),若在当前线程中执行th.join)方法,则当前线程暂停运行,等待另一线程th运行结束,才恢复到可运行状态。
(4)interrupt(),调用该方法可使线程从阻塞状态中恢复。
(5)currentThread(),该方法是静态方法,用于返回当前线程的引用。
(6)isAlive(),用于判断线程时候还处于活动状态。
(7)stop(),强行终止线程。
(8)suspend()和resume(),前者用于暂停一个线程,后者用于恢复该线程。
8.4线程的同步
考点:6
对象的加锁及其操作
多线程并发操作时,由于各线程对共享数据的操作顺序不同,会影响程序的执行结果,因此Java语言使传统的加锁技术对共享数据的操作进行并发控制。
1.对象加锁及其操作
临界区是指程序中的一个代码段,在这段代码中,单独并发的线程对同一个对象进行访问。在Java中,用关键字“synchronized”标识一个临界区。
对象锁是指Java为synchronized(Object)语句指定的对象进行加锁,对象锁是独占排他锁。
2.对象加锁的注意事项
使用对象锁需要注意如下几点:
(1)释放对象锁的情况:synchronized()语句块执行完,synchronized()语句块出现异常、持有锁的线程调用该对象的wait()方法。
(2)所有被访问的共享数据及访问代码都必须作为临界区,用synchronized加锁。
(3) synchronized标识的共享数据必须是私有的。可以把synchronized放在方法声明中,则该方法整个方法体都在临界区中,这种方式可读性好,但是对象锁的时间稍长。
(4)对象锁具有可重入性。可重入性是指在Java程序中,一个线程在持有一个对象锁的情况下可以再次请求并获得该对象的锁。可以用于避免死锁。
考点7:
避免死锁
死锁是指,程序中多个线程互相等待对方持有的锁,并且在得到对方锁之前不会释放自己的锁,从而造成线程的无限等待,最终死锁出现。
Java语言没有提供专门检测与避免死锁的机制,需要在程序中注意防止死锁的发生。在编写程序的时候一般可以按照如下规则避免死锁。
(1)加锁:从全局角度设计一个获得锁的顺序,在程序中都遵守这个顺序。
(2)解锁:按照加锁的反序进行。
考点:8
进程间通信的wait()和notify()
Java.lang.Object类提供了实现线程通信的2个方法wait()和notify()。
(1)wait(),某线程t1调用了一个对象。的wait()方法后,则t1被放入o的等待池中,并且t1释放o的锁。
(2) notify(),线程t2通过调用对象o的notify()方法,将对象o的等待池中的线程t1移入lock pool,在lockpool中,tl线程等待o的锁,一旦获得就可以运行。可以使用notifyAll()将等待池中的线程都移入lock pool。
(3)生产者、消费者问题,生产者是指产生资源或释放资源的线程,而消费者是指使用某类资源的线程。
(4)不建议使用的方法:stop(),suspend()和resume()。
8.5线程状态与生命周期
考点:9
线程状态与生命周期
线程的生命周期主要包括几个状态:新建、可运行(就绪)、运行、阻塞、终止状态。
(l)新建(new),创建一个线程,但是由于没有被分配系统资源,不马上执行。
(2)可运行(Runnable),线程调用start()方法,分配系统资源,线程进入Runnable状态。此时线程处于就绪状态,线程可以运行,但可能不在运行中。
(3)运行(Running),线程占有CPU进入实际运行的状态。若程序停止运行或线程正常结束,则线程进入终止状态;若线程执行了yield(),或者根据抢占调度策略导致线程进入可运行状态;若线程调用sleep()方法,join()方法、wait()方法、请求对象锁未获得时,或者线程中有I/O操作时,线程进入阻塞状态。
(4)阻塞状态(Blocked),包括:对象锁阻塞、等待阻塞和其他阻塞。其他阻塞是由于线程调用了sleep和join()方法而进入的状态,当sleep()时间到或者join ()执行的线程结束或等待时间到时,线程进入可运行状态对象锁阻塞是由于线程请求sychronized对象锁但是没有获得而进入的状态,若获得锁,则线程进入可运行状态等待阻寨是由于线程调用wait()方法而进入的状态,若被notifyAll()notify (),interrupt()中断或者等待时间到,则线程进入对象锁阻塞状态。
(5)终止状态(Dead),线程执行结束状态。
8.6线程相关的其他类与方法
考点:10
Java中的支持线程的类
在Java语言中,支持线程的类主要包括:Java.lang.Thread类java.lang.Runnable类,Java.lang.Objeect类,Java.lang.ThreadGroup类和Java.lang.ThreadDeath类。
考点:11
线程组
Java语言将一组线程定义为线程组,再将线程组作为一个对象进行统一的处理和维护,线程组由Java.lang.ThreadGroup类实现。
(1)一个线程组可以包含若干线程或其他线程组。
(2)创建线程时指定所属的线程组。
(3)Java应用程序是以main()作为根的线程组与线程的树形结构。
考点:12
Thread类中其他的方法
Thread类中包括如下一些有用的方法:
setName(),getName(),activeCount(),getThreadGroup(),setDaemon(),isDaemon(),toStrin(),enumerate()和check Access()方法等。
8.7对象的串行化
考点:13
串行化概念和目的
对象永久化是指将Java程序中的对象保存在外存中。
对象串行化是指对Java对象的读、写的过程,Java语言提供了对象流(2种字节流ObjectInputStream和ObjectOutputStream )和其他一些有关对象串行化的类和接口来支持对象的读写。
对象串行化一般用在下列情况:Java远程方法调用RMI和对象永久化。
考点:14
串行化对象的方法
1.将对象写到输出流
通过调用ObjectOutputStream类的writeObject()方法实现。
(1)由于ObjectOutputStream是过滤流类,构造对象流需要以其他的流作为基础。
(2) writeObject()方法通过递归将一个对象中引用的其他所有对象表示到输出流中,这样在串行化过程中,保持了对象之间的引用关系
(3)ObjectOutputStream实现了DataOutput接口。
(4)只有对象所对应的类实现了Serializable接口时,该对象才可以被串行化。
2.从对象输入流读取对象
通过调用ObjectInputStream类的readObject()方法实现。
从对象输入流读取对象的过程是将对象写到对象输出流的逆过程,由于readObject()方法的返回值是Object类的对象,因此要转化为串行化的类的对象时,需要使用强制类型转换。
考点:15
构造可串行化对象的类
可串行化对象的类必须实现Serializable接口,在类定义中使用implements Serializable子句来定义可串行化的类。
(1)对象串行化时,其所属类的方法与构造方法不被串行化。
(2)在可串行化的类中,静态变量和transient关键字修饰的数据不被串行化。
(3)可使用Java提供的默认串行化方法完成对象输入和输出操作。
(4)数据访问权限在串行化过程中不影响对象的输入和输出操作。
考点:16
定制串行化
定制串行化可以分为部分定制和完全定制两种。
1.部分定制串行化
部分定制串行化是指对于可串行化的类,定义自己的数据输入输出。包括对readObject()方法和writeObject()方法的重写,格式如下:
private void writeObject(ObjectOutputStream s)throws IOException
{
s.defaultWriteObject():
// 定制串行化代码
}
readObject()方法的定制格式与writeObject( )方法相同,定制这两个方法只能串行化直接的类,该类的父类的串行化处理是由系统自动进行的。
2.完全定制串行化
完全定制串行化是指对串行化类的所有数据(包括自定义的及父类中的数据)的输出全部进行定制编程时需要注意如下几点:
(1)完全定制串行化要求可串行化类实现Externalizable接口。
(2)通过实现writeExternal()方法来保存对象及其父类的数据或者状态。
(3)通过实现readExternal()方法来读取并恢复writeExternal()方法写入的对象数据,包括对象的父类。
(4)若对象串行化使用了自定义格式,writeExternal()和readExternal()方法都必须遵守该格式。
(5)必须定义一个public权限的构造方法,并且该方法不需要参数。
(6)若对象可保存的信息是敏感信息,则不建议使用上述方法。
考点:17
串行化中对敏感信息的保护以及注意事项
(1)保护敏感信息的手段是:对象中的敏感信息不从对象输入流中恢复,从对象流中恢复的敏感信息需要由类进行验证,可以通过定义敏感数据成员为private transient来实现,也可以通过在writeObject()和reanObject()方法中加入验证机制来保护敏感信息。过于敏感的信息,不能实现Externalizable和Serializable接口。
(2)对于瞬时状态对象的数据,要使用transient关键字标志;串行化后的对象需要进行加密处理,以保证数据的安全性。
考点:15
构造可串行化对象的类
可串行化对象的类必须实现Serializable接口,在类定义中使用implements Serializable子句来定义可串行化的类。
(1)对象串行化时,其所属类的方法与构造方法不被串行化。
(2)在可串行化的类中,静态变量和transient关键字修饰的数据不被串行化。
(3)可使用Java提供的默认串行化方法完成对象输入和输出操作。
(4)数据访问权限在串行化过程中不影响对象的输入和输出操作。
考点:16
定制串行化
定制串行化可以分为部分定制和完全定制两种。
1.部分定制串行化
部分定制串行化是指对于可串行化的类,定义自己的数据输入输出。包括对readObject()方法和writeObject()方法的重写,格式如下:
private void writeObject(ObjectOutputStream s)throws IOException
{
s.defaultWriteObject():
// 定制串行化代码
}
readObject()方法的定制格式与writeObject( )方法相同,定制这两个方法只能串行化直接的类,该类的父类的串行化处理是由系统自动进行的。
2.完全定制串行化
完全定制串行化是指对串行化类的所有数据(包括自定义的及父类中的数据)的输出全部进行定制编程时需要注意如下几点:
(1)完全定制串行化要求可串行化类实现Externalizable接口。
(2)通过实现writeExternal()方法来保存对象及其父类的数据或者状态。
(3)通过实现readExternal()方法来读取并恢复writeExternal()方法写入的对象数据,包括对象的父类。
(4)若对象串行化使用了自定义格式,writeExternal()和readExternal()方法都必须遵守该格式。
(5)必须定义一个public权限的构造方法,并且该方法不需要参数。
(6)若对象可保存的信息是敏感信息,则不建议使用上述方法。
考点:17
串行化中对敏感信息的保护以及注意事项
(1)保护敏感信息的手段是:对象中的敏感信息不从对象输入流中恢复,从对象流中恢复的敏感信息需要由类进行验证,可以通过定义敏感数据成员为private transient来实现,也可以通过在writeObject()和reanObject()方法中加入验证机制来保护敏感信息。过于敏感的信息,不能实现Externalizable和Serializable接口。
(2)对于瞬时状态对象的数据,要使用transient关键字标志;串行化后的对象需要进行加密处理,以保证数据的安全性。