1、Thread类大致声明
- 线程的基本属性
- 创建和结束线程的方法:一般推荐用继承 Runnable 接口
2、Thread 的基本属性和变量
3、yield( )、sleep( ) & init( )
yield()
- 主要用于测试,作用是暂停当前线程,以便其他线程有机会执行,不过不能指定暂停的时间,并且也不能保证当前线程马上停止
- 只能使同优先级或更高优先级的线程有执行的机会
sleep()
方法直接调用 native 方法,毫秒为入参- 作用是让当前线程暂停指定的时间(毫秒)
- 只是暂时让出CPU的执行权,并不释放锁。而wait方法则需要释放锁
init()
方法初始化各个属性值,所有构造器都调用init- 在编译生成class文件时,会自动产生两个方法,一个是类的初始化方法
<clinit>
, 另一个是实例的初始化方法<init>
<clinit>
:在jvm第一次加载 class 文件时调用,包括静态变量初始化语句和静态块的执行<init>
:在实例创建出来的时候调用,包括调用new操作符;调用 Class 或 java.llang.reflect.Constructor对象的newInstance()
方法;调用任何现有对象的 clone() 方法;通过 java.io.ObjectInputStream 类的 getObject() 方法反序列化。- 在编译生成class文件时,会自动产生两个方法,一个是类的初始化方法
4、start( ) 、run( ) & exit( )
start()
方法调用 nativestart0()
run()
方法在子类要重写
5、interrupt( ) 方法及区别
interrupt()
不会真正中断方法,只设置中断状态interrupted()
返回当前线程是否被中断,同时清除中断状态isInterrupted()
也用于判断线程是否中断,但是不会清除中断状态interrupted()
和isInterrupted()
都是调用同一个 native 方法,只不过入参不同
6、join( )
- 等待调用join方法的线程结束,再继续执行。如:t.join(),主要用于等待t线程运行结束
- 作用是父线程等待子线程执行完成后再执行,换句话说就是将异步执行的线程合并为串行的线程
这里主要调用了object的wait() 方法。我们可以看下wait() 方法的源码注释。
7、Object.wait( )
- 调用
wait()
方法的线程必须拥有该对象的监视器锁monitor
。 -
调用
wait()
之后,- 该线程会阻塞,进入等待队列,从而使别的线程有机会抢占该锁,等待其他线程调用“锁标志“对象的notify 或 notifyAll 方法恢复,或设定时间结束
- 同时释放 CPU 控制权和对象锁
-
wait 方法是一个本地方法,其底层是通过一个叫做监视器锁的对象来完成的,所以调用 wait 方式时必须获取到 monitor 对象的所有权,即通过 Synchronized 关键字,否则抛出 IllegalMonitorStateException 异常
wait()
方法又涉及到object.notify()
和object.notifyAll()
方法,这里可以看下他们的源码注释
8、Object.notify( )、Object.notifyAll( )
notify()
会选择等待队列中任意一个线程来唤醒。notifyAll()
会唤醒等待队列中所有线程,同等竞争该对象的锁。
为什么
wait()、notify()、notifyAll()
都需要在同步代码块中执行?- 因为这三个方法都需要获取对象的监视器才能调用。
-
在 Java中,所有对象都能够被作为"监视器monitor"——指一个拥有一个独占锁,一个入口队列和一个等待队列的实体entity。
所有对象的非同步 方法都能够在任意时刻被任意线程调用,此时不需要考虑加锁的问题。
而对于对象的同步方法来说,在任意时刻有且仅有一个拥有该对象独占锁的线程能够调用它们。例如,一个同步方法是独占的。如果在线程调用某一对象的同步方法时,对象的独占锁被其他线程拥有,那么当前线程将处于阻塞状态,并添加到对象的入口队列中。
9、不推荐使用的方法
- suspend() 和 resume()
这两个方法是配套使用的,suspend()是暂停线程,但并不释放资源,容易造成死锁情况
- stop()
因为调用stop会使线程释放所有的锁,导致不安全情况,在调用stop时候,由锁保护的临界区可能处于状态不一致的情况,这不一致状态将暴露给其他线程
推荐的做法是,维护一个状态变量,当线程需要停止时更改这一状态变量,该线程应检查这一状态变量,看该线程是否应该终止了