一、线程基础知识
1.创建线程的方式:
1.1 继承Thread类(实际上Thread也是实现Runnable接口)
1.2 实现Runnable接口
1.3 实现Callable接口(能返回执行结果)
1.4 通过线程池创建(通过Runnable或Callable参数)
2.线程的状态
2.1 初始:线程通过Thread td = new Thread()实例化的时候,开辟工作空间,创建线程。
2.2 就绪:线程进入一个可运行的状态,有四种方式进入此状态:
2.2.1 start()方法调用;
2.2.2 本来处于阻塞状态,后来解除阻塞;
2.2.3 一个正在运行的线程调用 yield() 方法会让线程重新进入就绪状态;(注意:如果调用 yield() 方法之后,没有其他等待执行的线程,此线程就会马上恢复执行)
2.2.4 JVM 本身将本地线程切换到了其他线程,那么这个线程就进入就绪状态。
2.3 运行中:线程从就绪状态获取到CPU的时间片,真正开始执行线程体的具体代码块(线程的run方法)。
2.4 等待:一个线程在等待另一个线程执行一个动作(没有特定的等待时间)。
2.5 超时等待:一个线程在一个特定的等待时间内等待另一个线程完成一个动作(有特定的等待时间)。
2.6 阻塞:一个线程在等待锁,比如线程进入一个synchronized方法,处于等待队列中。
2.7 死亡:线程体的代码执行完毕或者执行被中断。
2.7.1 程序正常执行完成
2.7.2 线程被关闭
2.7.3 线程出现错误
2.7.4 线程出现异常
*2.8 挂起:这是一个已经废弃的接口,程序通过调用suspend()进入挂起状态,通过resume()恢复就绪状态。挂起状态的线程没有释放对象锁。如果你用一个suspend挂起一个有锁的线程,那么只要线程没有resume,锁就不会释放。如果调用suspend的方法线程试图取得相同的锁,程序就会死锁。
3.Thread常用的函数
3.1 任务:
run函数:这个是线程运行的真正任务代码,需要开发人员去重写该方法,但是不能直接调用,直接调用只是执行了一个函数,不是运行线程。运行线程只能通过start方法。
3.2 开启:
start函数:这是线程的启动函数,调用这个函数之后会让JVM回调线程的run方法,实现线程的任务代码执行工作。
3.3 守护:
setDaemon函数:这个函数如果参数传的是true,那么在主线程(也就是main函数的对应线程)退出之后,这个函数对应的线程也会跟着退出。当一个JVM进程里面开启了多个线程时,这些的线程就会分成两种:守护线程和非守护线程。在Java中有一个规定:当所有的非守护线程退出之后,整个JVM进程就会退出。意思就是,守护线程不影响整个JVM进程的退出。例如:垃圾回收的线程就是守护线程,它们在后台默默工作,当开发者所有的非守护线程都退出之后,整个JVM进程就退出了。
3.4 阻塞:
join函数:阻塞其他的线程,等待当前join的线程执行完毕之后,才会继续执行其他的线程。
Object.wait函数:线程会释放对象的锁进入阻塞状态,只有当其他线程调用 notify /notifyAll函数时才可能唤醒此线程。
Thread.sleep函数:线程不会释放锁,只会阻塞线程,休眠参数对应的时间之后,恢复运行状态。
3.5 暂停:
yield函数:线程会让出自己的时间片给同优先级或更高优先级的线程去执行,自身线程回到就绪的状态,等待下次获取时间片继续执行。
3.6 唤醒:
Object.notify函数:当使用某个对象的notify()方法时,将从该对象的等待集合中选择一个等待的线程唤醒。
Object.notifyAll函数:唤醒所有处于等待状态的线程,然后让它们重新进入锁的争夺队列争抢锁,最终只有一个线程获得该锁,进行执行状态,其他线程仍要继续等待。
interrupted函数:这个函数名经常令人误解,字面理解为打断一个线程,实际上是唤醒一个线程,等于是给线程发送了一个唤醒的信号。
isInterrupted函数:这个函数是实例化后才能调用的函数,是用来判断线程是否收到过中断的信号,但是只读取中断的状态,不会去修改状态。
Thread.interrupted函数:这个是静态函数,也是用来判断线程是否收到过中断的信号,但是这个不仅读取中断的状态,还会重置中断的标志位。
3.7 打断:
interrupt函数:这个函数其实也一直让很多人误解,以为线程只要调用了这个函数,就会发生InterruptedException异常。实际只有那些声明了会抛出InterruptedException异常的函数在调用interrupt的时候才会抛出异常。目前有:
public static native void sleep(long millis) throws InterruptedException;(Thread的函数,包括其重载函数)
public final synchronized void join(long millis);(Thread的函数,包括其重载函数)
public final native void wait(long timeout) throws InterruptedException;(Object的函数)
3.8 关闭:
stop函数、destroy函数:这些函数都是官方不建议使用的函数,因为如果强制杀死线程,那么线程中使用的资源将不能正常关闭,比如网络连接、句柄、文件操作符等。所以,线程一旦开始运行,就没必要强制去关闭它。正确的做法应该是:让程序正常运行结束、释放完占用的资源之后退出运行。
4.如何优雅的实现让其他线程退出?
利用线程间的通信机制,通知其他线程退出。实际的开发中,一般都是通过一个关闭的标志位来实现进程的关闭。特别是有一些死循环的业务处理代码(比如轮询业务消息进行业务处理,就需要通过判断标志位实现退出)。需要注意的是,在退出之前需要处理好业务数据(比如未处理的数据存放到数据组件)和资源的释放工作(比如网络连接释放)。