4.2 守护(daemon)线程
- 线程分为 用户线程 和 守护线程
- JVM 虚拟机 必须 确保用户线程执行完毕
- JVM 不用等待 守护 线程执行完毕 < 守护线程跟 主线程和用户线程 不是一路子的。 >
- 如:后台记录操作日志,监控内存,垃圾回收等等。都是守护线程!
什么叫做 JVM 虚拟机 不等待 守护线程??
答:就是说 守护线程的 关闭和停止,不是 由 JVM 虚拟机决定的。而是 由 系统决定的。当程序真正的被停止后。或者说 当所有的用户线程和主线程 停止后。 守护线程 才会被 系统关闭和停止,而并非 JVM 虚拟机。JVM 虚拟机 只会 关闭和停止 用户线程,当用户线程执行的事务 完毕后(代码执行完了后),JVM 就会 帮这些用户线程 进行 自动的关闭 和停止!而 守护线程 是 看看 整个程序 是否还存在 用户线程 或 主线程 是否还有存在的意义。而决定 自己到底 是否还要 继续守护下去。
我们可以说,守护线程 是 后台线程,它的优先级 较低!整个程序 都不需要等待这个 线程 执行完成。这样 做的 意义就是 当子线程无限循环时,出现退不出程序的情况。也就是避免 孤儿线程。此时 你把 这个 线程 设定为 守护线程即可。切记 一些 需要进行 一直 死循环 监视的 线程,尽量 设为 守护线程!!!
package www.muquanyu.lesson03;public class DaemonDemo {
public static void main(String[] args) {
God god = new God();You you = new You();Thread A = new Thread(god);Thread B = new Thread(you);A.setDaemon(true);A.start();B.start();while(A.getState() != Thread.State.TERMINATED){
System.out.println("上帝--->"+A.getState());}System.out.println("上帝--->"+A.getState());}
}
//上帝(守护线程)
class God implements Runnable{
@Overridepublic void run() {
System.out.println("上帝保佑着你!上帝不会死的!");}
}//你(子线程)
class You implements Runnable{
@Overridepublic void run() {
for(int i = 0;i<100;++i){
System.out.println("你一生都开心的活着");}System.out.println("-====goodbye! world!====-");}
}
你会发现 God 上帝 只有短短的一行代码,但是 它这个 线程 好像一直没有消失!!!而是 等到 所有的线程甚至是 进程消失后,它才结束了自己的一生!
这样子写还不够明显,我们可以换一种方式,让上帝处理的代码变成 一个死循环!
package www.muquanyu.lesson03;public class DaemonDemo {
public static void main(String[] args) {
God god = new God();You you = new You();Thread A = new Thread(god);Thread B = new Thread(you);A.setDaemon(true);A.start();B.start();}
}
//上帝(守护线程)
class God implements Runnable{
@Overridepublic void run() {
while (true){
System.out.println("上帝保佑着你!上帝不会死的!");}}
}//你(子线程)
class You implements Runnable{
@Overridepublic void run() {
for(int i = 0;i<100;++i){
System.out.println("你一生都开心的活着");}System.out.println("-====goodbye! world!====-");}
}
4.3 线程同步机制
4.3.1 并发问题
- 并发:同一个对象 被 多个线程 同时操作!
所谓并发问题,就是 多个对象没有规则的去 操作同一个资源!这会导致 出现 资源乱抢 情况,场面一度混乱。你会发现 有的人 抢到的 资源 甚至 都不是 他需要的资源(资源分配错误),还可能会出现 多个对象 都说 那个资源是它们的!(资源分配重复)。
上述出现的情况是错误的,资源一共就那么点儿,所以不可能出现 重复的现象。也更不可能 出现 资源不对的情况!否则就属于 错误现象!
能解决 并发问题的方法 就是 线程同步
4.3.2 线程同步
-
现实生活中,我们会遇到 “同一个资源,多个人都想使用” 的问题,比如说 食堂排队在打饭,每个人都想要吃饭,但是必须遵守基本的道德准则和规范规则!! 否则你一闹事 可能会导致 大家 都吃不到饭了。
-
处理多线程问题的时候,多个线程访问同一个对象,并且某些线程还想去修改这个对象。这时候我们就需要线程同步。线程同步其实就是一种等待机制。 多个需要同时访问此对象的线程需要进入 这个 对象 的 “等待池” 形成队列,等待前面的线程使用完毕,下一个线程再去使用!(这样就有规则和规范,并且 效率也不会因此降低多少!!!)
那么 后面排队的人,也就是在 等待池里面的人,怎么会知道 前面的那个线程 正在 处理资源呢?这就涉及到 锁的 概念了!
4.3.3 队列和锁
前面三个同学,居然 并排 ?? 这不就是 典型的 线程并发问题吗?这是 错误的,不正确的,是不可取的。
- 队列必须伴随着锁
为什么呢?
答:比如说 你们排队上厕所,你进入一个 厕房,发现自己 没锁门,那肯定 会有人 拽一下门,看看到底锁没锁。如果有性质恶劣的人,甚至 会想着 把你 撵出来,他用这个 厕房!!!这就不对劲了。所以 队列 肯定 会伴随着 锁!
在讲 sleep 的时候,我们提到过,每个对象 都自带了一把锁,为了防止 自己进入队列后,没有 锁 对自己进行保护措施。而sleep 是无法 释放 掉 锁的!
这里的每个对象,指的并不是 线程!线程是被锁住的那个对象。比如 你上厕所,锁在哪里呢?肯定是厕房的锁呀!厕房的锁是提供给你用的,是把你锁在厕房里的!(此时厕房 属于 “同步监视器”)
4.3.3 synchronized 同步
-
由于统一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入 “锁机制” ,而当你使用 synchronized 同步的时候,就必须要提供一个对象来作为 “同步监视器”,审查每一个想使用它的线程对象,当一个 线程获得该对象的 的锁(排它锁)后,就会独占资源,其它线程必须等待该线程使用释放锁后(代码执行完就自动使用释放锁了),其他的线程才能继续进行对其资源的处理。
-
这可能会引起一些问题(降低性能的问题):
一个线程被 对象锁住后,会导致其它线程 挂起!
在多个线程竞争下,加锁和释放锁,会导致比较 频繁的切换 和 调度延时,这会 引起 性能问题。
如果一个 优先级高的线程 去等待一个 优先级 低的线程 去释放锁,那么这就演化成了 优先级的 倒置问题!是性能的 大问题。
这就说明了 “鱼和熊掌” 二者不能兼得 的道理。你想要性能 那么数据在大概率情况下,是避免不了危险的。你想要安全,那么性能在大概率情况下也是避免不了变慢的。