1、Object类的wait()、notify();Thread类中接口、synchronized关键字。
五种状态:
New 新建状态:线程被创建后就进入到了新建状态,Thread thread=new Thread();
Runnable 就绪状态:线程被创建后,其他线程调用该线程的start方法来启动该线程。处于就绪状态的线程,随时可能被CPU调度。
Running 运行状态:线程获取CPU执行。线程只能从就绪状态转为运行状态。
Blocked 阻塞状态: 线程因为某种原因放弃CPU的使用。只有直到线程进入到就绪状态,才有机会转入运行状态。
等待阻塞: 调用线程的wait()方法,让线程等待某工作完成。
同步阻塞: 线程在获取synchronized同步锁失败(同步锁被其他线程占用),会进入到同步阻塞状态。
其他阻塞: 通过调用线程的sleep()或join或发出IO请求。
Dead 死亡状态:线程执行完毕或者因为一场退出run()方法。
2、实现多线程的两种方式
实现runnable接口
继承Thread类,该类实现了runnable接口
Thread 和 Runnable 的相同点:都是“多线程的实现方式”。
Thread 和 Runnable 的不同点:
Thread 是类,而Runnable是接口;Thread本身是实现了Runnable接口的类。我们知道“一个类只能有一个父类,但是却能实现多个接口”,因此Runnable具有更好的扩展性。
此外,Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。
通常,建议通过“Runnable”实现多线程!
3、start()和run()的区别
start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。
run() : run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!
target是一个runnale对象,run()就是直接调用Thread的Runnable的run()方法。
4、Synchronized关键字
在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。
当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj)就获取了“obj这个对象”的同步锁。
不同线程对同步锁的访问是互斥的。
原则
第一条:当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
5、线程的等待与唤醒
在Object类中定义了wait()、notify()和notifyAll()等接口;
wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁;
而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程;
Object类中关于等待/唤醒的API详细信息如下:
notify() – 唤醒在此对象监视器上等待的单个线程。
notifyAll() – 唤醒在此对象监视器上等待的所有线程。
wait() – 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout) – 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout, int nanos) – 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。
6、线程让步
方法:yieId()
作用:让步,让当前的线程由运行状态进入到就绪状态,从而让其他具有相同优先级的线程获得执行权。但是不能保证其他线程就一定能获得执行权,也有可能是当前线程又进入到运行状态继续运行。
yieId和wait区别:
(01) wait()是让线程由“运行状态”进入到“等待(阻塞)状态”,而不yield()是让线程由“运行状态”进入到“就绪状态”。
(02) wait()是会线程释放它所持有对象的同步锁,而yield()方法不会释放锁。
7、线程休眠
sleep在一下Thread类中,作用是让当前的线程休眠,即当前线程会从运行状态进入到阻塞状态。当时间到了的时候,会从阻塞状态变为就绪状态,等待CPU调度。
sleep和wait的比较:
wait的作用是让当前线程由运行状态进入到阻塞状态,同时释放掉同步锁。而sleep的作用也是让当前线程进入到阻塞状态,但是不会释放掉同步锁。
8、join
join()定义在Thread类中。
作用为:让“主线程”等待“子线程”结束之后才运行。
9、interrupt()和线程终止方式
interrupt()的作用是中断本线程。本线程中断自己是被允许的,其他线程调用本线程的interrupt方法的时候,会通过checkAccess()检查权限,这有可能抛出SecurityException异常。
通常我们会通过中断方式终止处于“阻塞状态”的线程。
当线程由于调用了sleep()、wait()、join()等方法而进入到阻塞状态;此时若调用线程的interrupt()方法将线程的中断标记设为了true,由于处于阻塞状态,中断标记会被清除,同时产生一个interruptException异常。将interruptException放在适当的位置就能终止线程;
interrupt()并不会终止“运行状态”的线程,它会将线程的中断标记设为true。(通过isInterrupted()方法判断线程是不是处于中断状态)。
终止线程的通用写法:
@Override
public void run() {
try {
// 1. isInterrupted()保证,只要中断标记为true就终止线程。
while (!isInterrupted()) {
// 执行任务...
}
} catch (InterruptedException ie) {
// 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。
}
}
interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。
区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。
10、线程优先级和守护线程
Java 中的线程的优先级是1—10,默认优先级是5。
Java中有两种线程,用户线程和守护线程。可以通过isDaemon()(守护线程)方法来区别。用户线程一般执行用户级线程,而守护线程也就是后台线程。
PS:Java虚拟机在“用户线程”都结束后退出。