线程基础
基础概念
-
进程
程序运行资源分配的最小单位
-
线程
CPU调度的最小单位,必须依赖于进程而存在
-
CPU核心数和线程数的关系
一般情况下是1:1对应关系,Intel引入超线程后,为1:2的关系
-
CPU时间片轮转机制
又称RR调度,时间片太短会导致过多的进程切换,降低CPU效率;时间片太长又可能引起响应变差
-
并行和并发
并行是同时执行;并发是交替执行,要加上单位时间才有意义
-
高并发编程的好处
充分利用CPU的资源
加快响应用户的时间
可以使得代码模块化,异步化,简单化
-
多线程程序注意事项
-
线程之间的安全性
多个线程同时执行写操作,一般都需要考虑线程同步
线程之间的死锁
-
线程太多会将服务器资源耗尽形成死机宕机
线程数大,或某个资源的候选线程数远远超过可用资源数最好使用资源池
-
Java线程
Java程序天生是多线程
执行main方法的是一个名称为main的线程;还存在其他的守护线程
线程的启动与中止
启动
-
启动线程的方式
X extends Thread ==> X.start
X implements Runnable ==> 交给Thread运行
-
Thread和Runnable的区别
Thread是Java对线程的唯一抽象;Runnable只是对任务的抽象。Thread可以接受任意一个Runnable的实例并执行
-
run()和start()的区别
start()方法让一个线程进入就绪队列等待分配CPU,分到CPU后才调用实现的run()方法,start()方法不能重复调用,否则会抛出异常
run()方法是业务逻辑实现的地方,本质上和普通成员方法没有任何区别,可以重复执行,页可以被单独调用
中止
-
线程自然终止
run执行完成,或抛出未处理的异常
-
stop
suspend()、resume()和stop()这些API被标志为过期的,不建议使用,因为可能导致占有的资源无法正常释放,导致死锁或程序工作在不确定状态下
-
中断
-
interrupt()
对某个线程进行中断操作。Java线程是协作式的,不是抢占式的,线程通过检查自身的中断标识位进行响应
-
isInterrupted()
判断是否被中断
-
Thread.interrupted()
静态方法,判断当前线程是否被中断,同时将中断标识位改为false
-
如果线程处于阻塞状态(thread.sleep、thread.join、thread.wait等)
中断标识位为true时,会抛出InterruptedException异常,并立即将线程的中断标识位清除
-
不建议自定义一个取消标识位来中止线程的运行
因为run方法有阻塞调用时无法很快检测到取消标识位
注意,处于死锁的线程无法被中断
-
其他线程相关的方法
yield()方法
使当前线程让出CPU占有权,但让出的时间不可设定,也不会释放锁资源
执行yield()的线程有可能进入到就绪状态后会被操作系统再次选中马上又被执行
wait()/notify()/notifyAll()
参考线程间的共享与协作
join方法
可以将两个交替执行的线程合并为顺序执行
线程的生命周期

线程的优先级
通过一个整型成员变量priority来控制优先级,范围为1~10
在线程创建的时候可以通过setPriority(int)方法修改优先级,默认优先级是5,优先级高的线程分配时间片的数量要多于优先级低的线程
针对I/O线程设置较高优先级,计算线程设置较低优先级
不同的JVM以及操作系统,线程规划会存在差异,有些操作系统甚至会忽略对线程优先级的设定
不能依赖线程优先级进行编程
守护线程
是一种支持型线程,主要被用作程序中后台调度以及支持性工作。当一个Java虚拟机中不存在非Daemon线程时,Java虚拟机会退出。
可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程
Java虚拟机退出时,Daemon线程中的finally块并不一定会执行,不能依赖finally块中的内容确保执行关闭或清理资源的逻辑